def point3d(v, size=5, **kwds): """ Plot a point or list of points in 3d space. INPUT: - ``v`` - a point or list of points - ``size`` - (default: 5) size of the point (or points) - ``color`` - a word that describes a color - ``rgbcolor`` - (r,g,b) with r, g, b between 0 and 1 that describes a color - ``opacity`` - (default: 1) if less than 1 then is transparent EXAMPLES:: sage: sum([point3d((i,i^2,i^3), size=5) for i in range(10)]) We check to make sure this works with vectors:: sage: pl = point3d([vector(ZZ,(1, 0, 0)), vector(ZZ,(0, 1, 0)), (-1, -1, 0)]) sage: p = point(vector((2,3,4))) sage: print p Graphics3d Object We check to make sure the options work:: sage: point3d((4,3,2),size=20,color='red',opacity=.5) """ if ( (isinstance(v, (list, tuple)) or is_Vector(v)) and len(v) == 3 and not (isinstance(v[0], (list, tuple)) or is_Vector(v[0])) ): return Point(v, size, **kwds) else: A = sum([Point(z, size, **kwds) for z in v]) A._set_extra_kwds(kwds) return A
def eval(self, Vobj): r""" Evaluates the left hand side `A\vec{x}+b` on the given vertex/ray/line. NOTES: * Evaluating on a vertex returns `A\vec{x}+b` * Evaluating on a ray returns `A\vec{r}`. Only the sign or whether it is zero is meaningful. * Evaluating on a line returns `A\vec{l}`. Only whether it is zero or not is meaningful. EXAMPLES:: sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[-1,-1]]) sage: ineq = triangle.inequality_generator().next() sage: ineq An inequality (2, -1) x + 1 >= 0 sage: [ ineq.eval(v) for v in triangle.vertex_generator() ] [0, 0, 3] sage: [ ineq * v for v in triangle.vertex_generator() ] [0, 0, 3] If you pass a vector, it is assumed to be the coordinate vector of a point:: sage: ineq.eval( vector(ZZ, [3,2]) ) 5 """ if is_Vector(Vobj): return self.A() * Vobj + self.b() return Vobj.evaluated_on(self)
def point(self, v, check=True): r""" Constructs a point on ``self`` corresponding to the input ``v``. If ``check`` is True, then checks if ``v`` defines a valid point on ``self``. If no rational point on ``self`` is known yet, then also caches the point for use by ``self.rational_point()`` and ``self.parametrization()``. EXAMPLES :: sage: c = Conic([1, -1, 1]) sage: c.point([15, 17, 8]) (15/8 : 17/8 : 1) sage: c.rational_point() (15/8 : 17/8 : 1) sage: d = Conic([1, -1, 1]) sage: d.rational_point() (1 : 1 : 0) """ if is_Vector(v): v = Sequence(v) p = ProjectiveCurve_generic.point(self, v, check=check) if self._rational_point is None: self._rational_point = p return p
def point(self, v, check=True): r""" Constructs a point on ``self`` corresponding to the input ``v``. If ``check`` is True, then checks if ``v`` defines a valid point on ``self``. If no rational point on ``self`` is known yet, then also caches the point for use by ``self.rational_point()`` and ``self.parametrization()``. EXAMPLES :: sage: c = Conic([1, -1, 1]) sage: c.point([15, 17, 8]) (15/8 : 17/8 : 1) sage: c.rational_point() (15/8 : 17/8 : 1) sage: d = Conic([1, -1, 1]) sage: d.rational_point() (-1 : 1 : 0) """ if is_Vector(v): v = Sequence(v) p = ProjectivePlaneCurve.point(self, v, check=check) if self._rational_point is None: self._rational_point = p return p
def eval(self, Vobj): r""" Evaluates the left hand side `A\vec{x}+b` on the given vertex/ray/line. NOTES: * Evaluating on a vertex returns `A\vec{x}+b` * Evaluating on a ray returns `A\vec{r}`. Only the sign or whether it is zero is meaningful. * Evaluating on a line returns `A\vec{l}`. Only whether it is zero or not is meaningful. EXAMPLES:: sage: triangle = Polyhedron(vertices=[[1,0],[0,1],[-1,-1]]) sage: ineq = next(triangle.inequality_generator()) sage: ineq An inequality (2, -1) x + 1 >= 0 sage: [ ineq.eval(v) for v in triangle.vertex_generator() ] [0, 0, 3] sage: [ ineq * v for v in triangle.vertex_generator() ] [0, 0, 3] If you pass a vector, it is assumed to be the coordinate vector of a point:: sage: ineq.eval( vector(ZZ, [3,2]) ) 5 """ if is_Vector(Vobj): return self.A() * Vobj + self.b() return Vobj.evaluated_on(self)
def __init__(self, A, elt=None, check=True): """ TESTS:: sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1,0], [0,1]]), Matrix([[0,1], [0,0]])]) sage: A(QQ(4)) Traceback (most recent call last): ... TypeError: elt should be a vector, a matrix, or an element of the base field sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1], [-1,0]])]) sage: elt = B(Matrix([[1,1], [-1,1]])); elt e0 + e1 sage: TestSuite(elt).run() sage: B(Matrix([[0,1], [1,0]])) Traceback (most recent call last): ... ValueError: matrix does not define an element of the algebra """ AlgebraElement.__init__(self, A) k = A.base_ring() n = A.degree() if elt is None: self._vector = vector(k, n) self._matrix = Matrix(k, n) else: if isinstance(elt, int): elt = Integer(elt) elif isinstance(elt, list): elt = vector(elt) if A == elt.parent(): self._vector = elt._vector.base_extend(k) self._matrix = elt._matrix.base_extend(k) elif k.has_coerce_map_from(elt.parent()): e = k(elt) if e == 0: self._vector = vector(k, n) self._matrix = Matrix(k, n) elif A.is_unitary(): self._vector = A._one * e self._matrix = Matrix.identity(k, n) * e else: raise TypeError("algebra is not unitary") elif is_Vector(elt): self._vector = elt.base_extend(k) self._matrix = Matrix( k, sum([elt[i] * A.table()[i] for i in range(n)])) elif is_Matrix(elt): if not A.is_unitary(): raise TypeError("algebra is not unitary") self._vector = A._one * elt if not check or sum( [self._vector[i] * A.table()[i] for i in range(n)]) == elt: self._matrix = elt else: raise ValueError( "matrix does not define an element of the algebra") else: raise TypeError("elt should be a vector, a matrix, " + "or an element of the base field")
def rank_weight(c, sub_field=None, basis=None): r""" Return the rank of ``c`` as a matrix over ``sub_field``. If ``c`` is a vector over some field `F_{q^m}`, the function converts it into a matrix over `F_q`. INPUT: - ``c`` -- a vector over some field `F_{q^m}`; or a matrix over `F_q` - ``sub_field`` -- (default: ``None``) a sub field of `F_{q^m}`. If not specified, it is the prime subfield `F_p` of `F_{q^m}`. - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over ``sub_field``. If not specified, given that `q = p^s`, let `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. EXAMPLES:: sage: from sage.coding.linear_rank_metric import rank_weight sage: x = GF(64).gen() sage: a = vector(GF(64), (x + 1, x + 1, 1)) sage: rank_weight(a, GF(4)) 2 """ if is_Vector(c): c = to_matrix_representation(c, sub_field, basis) return c.rank()
def pyobject(self, ex, obj): from mathics.core import expression from mathics.core.expression import Number if obj is None: return expression.Symbol('Null') elif isinstance(obj, (list, tuple)) or is_Vector(obj): return expression.Expression('List', *(from_sage(item, self.subs) for item in obj)) elif isinstance(obj, Constant): return expression.Symbol(obj._conversions.get('mathematica', obj._name)) elif is_Integer(obj): return expression.Integer(str(obj)) elif isinstance(obj, sage.Rational): rational = expression.Rational(str(obj)) if rational.value.denom() == 1: return expression.Integer(rational.value.numer()) else: return rational elif isinstance(obj, sage.RealDoubleElement) or is_RealNumber(obj): return expression.Real(str(obj)) elif is_ComplexNumber(obj): real = Number.from_string(str(obj.real())).value imag = Number.from_string(str(obj.imag())).value return expression.Complex(real, imag) elif isinstance(obj, NumberFieldElement_quadratic): # TODO: this need not be a complex number, but we assume so! real = Number.from_string(str(obj.real())).value imag = Number.from_string(str(obj.imag())).value return expression.Complex(real, imag) else: return expression.from_python(obj)
def __init__(self, parent, matrix, inhomogeneous, is_zero=lambda p: False, relations=[]): ## Checking the input of matrix and vector if (not SAGE_element.is_Matrix(matrix)): matrix = Matrix(matrix) if (not SAGE_element.is_Vector(inhomogeneous)): inhomogeneous = vector(inhomogeneous) if (isinstance(parent, Wrap_w_Sequence_Ring)): parent = parent.base() ## Building the parent of the matrix and the vector pmatrix = matrix.parent().base() pinhom = inhomogeneous.parent().base() ## Setting the variables for the matrix, the parent and the vector self.__parent = pushout(pmatrix, pinhom) self.__matrix = matrix.change_ring(self.__parent) self.__inhomogeneous = inhomogeneous.change_ring(self.__parent) ## Setting the parent for the solutions if (not pushout(parent, self.__parent) == parent): try: self.__matrix = self.__matrix.change_ring(parent) self.__inhomogeneous = self.__inhomogeneous.change_ring(parent) self.__parent = parent except: raise TypeError( "The parent for the solutions must be an extension of the parent for the input" ) self.__solution_parent = parent ## Setting other variables if (relations is None): relations = [] self.__relations = [self.__parent(el) for el in relations] try: self.__gb = ideal(self.parent(), self.__relations).groebner_basis() except AttributeError: try: self.__gb = [ideal(self.parent(), self.__relations).gen()] except: self.__gb = [0] self.__is_zero = is_zero ## Creating the variables for the echelon form self.__echelon = None self.__transformation = None ## Creating the variables for the solutions self.__solution = None self.__syzygy = None
def __init__(self, A, elt=None, check=True): """ TESTS:: sage: A = FiniteDimensionalAlgebra(GF(3), [Matrix([[1,0], [0,1]]), Matrix([[0,1], [0,0]])]) sage: A(QQ(4)) Traceback (most recent call last): ... TypeError: elt should be a vector, a matrix, or an element of the base field sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1,0], [0,1]]), Matrix([[0,1], [-1,0]])]) sage: elt = B(Matrix([[1,1], [-1,1]])); elt e0 + e1 sage: TestSuite(elt).run() sage: B(Matrix([[0,1], [1,0]])) Traceback (most recent call last): ... ValueError: matrix does not define an element of the algebra """ AlgebraElement.__init__(self, A) k = A.base_ring() n = A.degree() if elt is None: self._vector = vector(k, n) self._matrix = Matrix(k, n) else: if isinstance(elt, int): elt = Integer(elt) elif isinstance(elt, list): elt = vector(elt) if A == elt.parent(): self._vector = elt._vector.base_extend(k) self._matrix = elt._matrix.base_extend(k) elif k.has_coerce_map_from(elt.parent()): e = k(elt) if e == 0: self._vector = vector(k, n) self._matrix = Matrix(k, n) elif A.is_unitary(): self._vector = A._one * e self._matrix = Matrix.identity(k, n) * e else: raise TypeError("algebra is not unitary") elif is_Vector(elt): self._vector = elt.base_extend(k) self._matrix = Matrix(k, sum([elt[i] * A.table()[i] for i in xrange(n)])) elif is_Matrix(elt): if not A.is_unitary(): raise TypeError("algebra is not unitary") self._vector = A._one * elt if not check or sum([self._vector[i]*A.table()[i] for i in xrange(n)]) == elt: self._matrix = elt else: raise ValueError("matrix does not define an element of the algebra") else: raise TypeError("elt should be a vector, a matrix, " + "or an element of the base field")
def jacobian(functions, variables): """ Return the Jacobian matrix, which is the matrix of partial derivatives in which the i,j entry of the Jacobian matrix is the partial derivative diff(functions[i], variables[j]). EXAMPLES:: sage: x,y = var('x,y') sage: g=x^2-2*x*y sage: jacobian(g, (x,y)) [2*x - 2*y -2*x] The Jacobian of the Jacobian should give us the "second derivative", which is the Hessian matrix:: sage: jacobian(jacobian(g, (x,y)), (x,y)) [ 2 -2] [-2 0] sage: g.hessian() [ 2 -2] [-2 0] sage: f=(x^3*sin(y), cos(x)*sin(y), exp(x)) sage: jacobian(f, (x,y)) [ 3*x^2*sin(y) x^3*cos(y)] [-sin(x)*sin(y) cos(x)*cos(y)] [ e^x 0] sage: jacobian(f, (y,x)) [ x^3*cos(y) 3*x^2*sin(y)] [ cos(x)*cos(y) -sin(x)*sin(y)] [ 0 e^x] """ if is_Matrix(functions) and (functions.nrows() == 1 or functions.ncols() == 1): functions = functions.list() elif not (isinstance(functions, (tuple, list)) or is_Vector(functions)): functions = [functions] if not isinstance(variables, (tuple, list)) and not is_Vector(variables): variables = [variables] return matrix([[diff(f, v) for v in variables] for f in functions])
def jacobian(functions, variables): """ Return the Jacobian matrix, which is the matrix of partial derivatives in which the i,j entry of the Jacobian matrix is the partial derivative diff(functions[i], variables[j]). EXAMPLES:: sage: x,y = var('x,y') sage: g=x^2-2*x*y sage: jacobian(g, (x,y)) [2*x - 2*y -2*x] The Jacobian of the Jacobian should give us the "second derivative", which is the Hessian matrix:: sage: jacobian(jacobian(g, (x,y)), (x,y)) [ 2 -2] [-2 0] sage: g.hessian() [ 2 -2] [-2 0] sage: f=(x^3*sin(y), cos(x)*sin(y), exp(x)) sage: jacobian(f, (x,y)) [ 3*x^2*sin(y) x^3*cos(y)] [-sin(x)*sin(y) cos(x)*cos(y)] [ e^x 0] sage: jacobian(f, (y,x)) [ x^3*cos(y) 3*x^2*sin(y)] [ cos(x)*cos(y) -sin(x)*sin(y)] [ 0 e^x] """ if is_Matrix(functions) and (functions.nrows()==1 or functions.ncols()==1): functions = functions.list() elif not (isinstance(functions, (tuple, list)) or is_Vector(functions)): functions = [functions] if not isinstance(variables, (tuple, list)) and not is_Vector(variables): variables = [variables] return matrix([[diff(f, v) for v in variables] for f in functions])
def to_matrix_representation(v, sub_field=None, basis=None): r""" Return a matrix representation of ``v`` over ``sub_field`` in terms of ``basis``. Let `(b_1, b_2, \ldots, b_m)`, `b_i \in GF(q^m)`, be a basis of `GF(q^m)` as a vector space over `GF(q)`. Take an element `x \in GF(q^m)`. We can write `x` as `x = u_1 b_1 + u_2 b_2 + \ldots u_m b_m`, where `u_i \in GF(q)`. This way we can represent an element from `GF(q^m)` as a vector of length `m` over `GF(q)`. Given a vector ``v`` of length `n` over some field `F_{q^m}`, we can represent each entry as a vector of length `m`, yielding an `m \times n` matrix over ``sub_field``. In case ``sub_field`` is not given, we take the prime subfield `F_p` of `F_{q^m}`. INPUT: - ``v`` -- a vector over some field `F_{q^m}` - ``sub_field`` -- (default: ``None``) a sub field of `F_{q^m}`. If not specified, it is the prime subfield `F_p` of `F_{q^m}`. - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over ``sub_field``. If not specified, given that `q = p^s`, let `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. EXAMPLES:: sage: from sage.coding.linear_rank_metric import to_matrix_representation sage: x = GF(64).gen() sage: a = vector(GF(64), (x + 1, x + 1, 1)) sage: to_matrix_representation(a, GF(4)) [1 1 1] [1 1 0] [0 0 0] sage: m = Matrix(GF(4), [[1, 1, 1], [1, 1, 0], [0, 0, 0]]) sage: to_matrix_representation(m) Traceback (most recent call last): ... TypeError: Input must be a vector """ if not is_Vector(v): raise TypeError("Input must be a vector") base_field = v.base_ring() if not sub_field: sub_field = base_field.prime_subfield() n = v.length() m = base_field.degree() // sub_field.degree() extension, to_big_field, from_big_field = base_field.vector_space( sub_field, basis, map=True) return Matrix(sub_field, m, n, lambda i, j: from_big_field(v[j])[i])
def int_tuple_list_vector(v): if isinstance(v, int): return v if isinstance(v, Integer): return int(v) if isinstance(v, tuple): return Bin(v).int if isinstance(v, list): return Bin(v).int if is_Vector(v): return Bin(tuple(v)).int raise TypeError("%r is not tuple, list, vector or integer")
def point3d(v, size=5, **kwds): """ Plot a point or list of points in 3d space. INPUT: - ``v`` - a point or list of points - ``size`` - (default: 5) size of the point (or points) - ``color`` - a word that describes a color - ``rgbcolor`` - (r,g,b) with r, g, b between 0 and 1 that describes a color - ``opacity`` - (default: 1) if less than 1 then is transparent EXAMPLES:: sage: sum([point3d((i,i^2,i^3), size=5) for i in range(10)]) We check to make sure this works with vectors:: sage: pl = point3d([vector(ZZ,(1, 0, 0)), vector(ZZ,(0, 1, 0)), (-1, -1, 0)]) sage: p = point(vector((2,3,4))) sage: print p Graphics3d Object We check to make sure the options work:: sage: point3d((4,3,2),size=20,color='red',opacity=.5) """ if (isinstance(v,(list,tuple)) or is_Vector(v)) and len(v) == 3 and not (isinstance(v[0],(list,tuple)) or is_Vector(v[0])): return Point(v, size, **kwds) else: A = sum([Point(z, size, **kwds) for z in v]) A._set_extra_kwds(kwds) return A
def __init__(self, A, B, Title, C=[]): if not A.parent().is_exact(): raise MustBeExact("RungeKutta: parent of A is not exact") if not B.parent().is_exact(): raise MustBeExact("RungeKutta: parent of B is not exact") if not is_Matrix(A): raise NotA("RungeKutta: A is not a matrix") if not is_Vector(B): raise NotA("RungeKutta: B is not a vector") if A.dimensions()[0] != A.dimensions()[1]\ or A.dimensions()[0] != len(B): raise DimensionsAreIncompatible(A, B, C) if C != [] and len(C) != A.dimensions()[0]: raise DimensionsAreIncompatible(A, B, C) self.A = A self.B = B self.Title = Title self.C = C
def simplify(self, obj): r''' Method to simplify an object using the relations found. This method simplifies an object (either an element, vector, list, tuple or matrix) using the relations that we found during the computation of the solutions of this linear system. WARNING: repeated executions of this method may return different outputs since we may have found more relations. ''' if (SAGE_element.is_Matrix(obj)): return Matrix(obj.parent().base(), [[self.simplify(el) for el in row] for row in obj]) elif (SAGE_element.is_Vector(obj)): return vector(obj.parent().base(), [self.simplify(el) for el in obj]) elif (isinstance(obj, list)): return [self.simplify(el) for el in obj] elif (isinstance(obj, tuple)): return tuple([self.simplify(el) for el in obj]) elif (self.have_ideal()): return obj.reduce(self.__gb) return obj
def rank_distance(a, b, sub_field=None, basis=None): r""" Return the rank of ``a`` - ``b`` as a matrix over ``sub_field``. Take two vectors ``a``, ``b`` over some field `F_{q^m}`. This function converts them to matrices over `F_q` and calculates the rank of their difference. If ``sub_field`` is not specified, we take the prime subfield `F_q` of `F_{q^m}`. INPUT: - ``a`` -- a vector over some field `F_{q^m}` - ``b`` -- a vector over some field `F_{q^m}` - ``sub_field`` -- (default: ``None``) a sub field of `F_{q^m}`. If not specified, it is the prime subfield `F_p` of `F_{q^m}`. - ``basis`` -- (default: ``None``) a basis of `F_{q^m}` as a vector space over ``sub_field``. If not specified, given that `q = p^s`, let `1,\beta,\ldots,\beta^{sm}` be the power basis that SageMath uses to represent `F_{q^m}`. The default basis is then `1,\beta,\ldots,\beta^{m-1}`. EXAMPLES:: sage: from sage.coding.linear_rank_metric import rank_distance sage: x = GF(64).gen() sage: a = vector(GF(64), (x + 1, x + 1, 1)) sage: b = vector(GF(64), (1, 0, 0)) sage: rank_distance(a, b, GF(4)) 2 sage: c = vector(GF(4), (1, 0, 0)) sage: rank_distance(a, c, GF(4)) Traceback (most recent call last): ... ValueError: The base field of (z6 + 1, z6 + 1, 1) and (1, 0, 0) has to be the same sage: d = Matrix(GF(64), (1, 0, 0)) sage: rank_distance(a, d, GF(64)) Traceback (most recent call last): ... TypeError: Both inputs have to be vectors sage: e = vector(GF(64), (1, 0)) sage: rank_distance(a, e, GF(64)) Traceback (most recent call last): ... ValueError: The length of (z6 + 1, z6 + 1, 1) and (1, 0) has to be the same """ if not (a.base_ring() == b.base_ring()): raise ValueError( "The base field of {} and {} has to be the same".format(a, b)) if not (is_Vector(a) and is_Vector(b)): raise TypeError("Both inputs have to be vectors") if not len(a) == len(b): raise ValueError("The length of {} and {} has to be the same".format( a, b)) a = to_matrix_representation(a, sub_field, basis) b = to_matrix_representation(b, sub_field, basis) return (a - b).rank()
def parametric_plot3d(f, urange, vrange=None, plot_points="automatic", boundary_style=None, **kwds): r""" Return a parametric three-dimensional space curve or surface. There are four ways to call this function: - ``parametric_plot3d([f_x, f_y, f_z], (u_min, u_max))``: `f_x, f_y, f_z` are three functions and `u_{\min}` and `u_{\max}` are real numbers - ``parametric_plot3d([f_x, f_y, f_z], (u, u_min, u_max))``: `f_x, f_y, f_z` can be viewed as functions of `u` - ``parametric_plot3d([f_x, f_y, f_z], (u_min, u_max), (v_min, v_max))``: `f_x, f_y, f_z` are each functions of two variables - ``parametric_plot3d([f_x, f_y, f_z], (u, u_min, u_max), (v, v_min, v_max))``: `f_x, f_y, f_z` can be viewed as functions of `u` and `v` INPUT: - ``f`` - a 3-tuple of functions or expressions, or vector of size 3 - ``urange`` - a 2-tuple (u_min, u_max) or a 3-tuple (u, u_min, u_max) - ``vrange`` - (optional - only used for surfaces) a 2-tuple (v_min, v_max) or a 3-tuple (v, v_min, v_max) - ``plot_points`` - (default: "automatic", which is 75 for curves and [40,40] for surfaces) initial number of sample points in each parameter; an integer for a curve, and a pair of integers for a surface. - ``boundary_style`` - (default: None, no boundary) a dict that describes how to draw the boundaries of regions by giving options that are passed to the line3d command. - ``mesh`` - bool (default: False) whether to display mesh grid lines - ``dots`` - bool (default: False) whether to display dots at mesh grid points .. note:: #. By default for a curve any points where `f_x`, `f_y`, or `f_z` do not evaluate to a real number are skipped. #. Currently for a surface `f_x`, `f_y`, and `f_z` have to be defined everywhere. This will change. #. mesh and dots are not supported when using the Tachyon ray tracer renderer. EXAMPLES: We demonstrate each of the four ways to call this function. #. A space curve defined by three functions of 1 variable: :: sage: parametric_plot3d( (sin, cos, lambda u: u/10), (0, 20)) Note above the lambda function, which creates a callable Python function that sends `u` to `u/10`. #. Next we draw the same plot as above, but using symbolic functions: :: sage: u = var('u') sage: parametric_plot3d( (sin(u), cos(u), u/10), (u, 0, 20)) #. We draw a parametric surface using 3 Python functions (defined using lambda): :: sage: f = (lambda u,v: cos(u), lambda u,v: sin(u)+cos(v), lambda u,v: sin(v)) sage: parametric_plot3d(f, (0, 2*pi), (-pi, pi)) #. The surface, but with a mesh: :: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u) + cos(v), sin(v)), (u, 0, 2*pi), (v, -pi, pi), mesh=True) #. The same surface, but where the defining functions are symbolic: :: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u) + cos(v), sin(v)), (u, 0, 2*pi), (v, -pi, pi)) We increase the number of plot points, and make the surface green and transparent: :: sage: parametric_plot3d((cos(u), sin(u) + cos(v), sin(v)), (u, 0, 2*pi), (v, -pi, pi), color='green', opacity=0.1, plot_points=[30,30]) We call the space curve function but with polynomials instead of symbolic variables. :: sage: R.<t> = RDF[] sage: parametric_plot3d( (t, t^2, t^3), (t, 0, 3) ) Next we plot the same curve, but because we use (0, 3) instead of (t, 0, 3), each polynomial is viewed as a callable function of one variable:: sage: parametric_plot3d( (t, t^2, t^3), (0, 3) ) We do a plot but mix a symbolic input, and an integer:: sage: t = var('t') sage: parametric_plot3d( (1, sin(t), cos(t)), (t, 0, 3) ) We specify a boundary style to show us the values of the function at its extrema:: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u) + cos(v), sin(v)), (u, 0, pi), (v, 0, pi), \ ... boundary_style={"color": "black", "thickness": 2}) We can plot vectors:: sage: x,y=var('x,y') sage: parametric_plot3d(vector([x-y,x*y,x*cos(y)]), (x,0,2), (y,0,2)) sage: t=var('t') sage: p=vector([1,2,3]) sage: q=vector([2,-1,2]) sage: parametric_plot3d(p*t+q, (t, 0, 2)) Any options you would normally use to specify the appearance of a curve are valid as entries in the boundary_style dict. MANY MORE EXAMPLES: We plot two interlinked tori:: sage: u, v = var('u,v') sage: f1 = (4+(3+cos(v))*sin(u), 4+(3+cos(v))*cos(u), 4+sin(v)) sage: f2 = (8+(3+cos(v))*cos(u), 3+sin(v), 4+(3+cos(v))*sin(u)) sage: p1 = parametric_plot3d(f1, (u,0,2*pi), (v,0,2*pi), texture="red") sage: p2 = parametric_plot3d(f2, (u,0,2*pi), (v,0,2*pi), texture="blue") sage: p1 + p2 A cylindrical Star of David:: sage: u,v = var('u v') sage: f_x = cos(u)*cos(v)*(abs(cos(3*v/4))^500 + abs(sin(3*v/4))^500)^(-1/260)*(abs(cos(4*u/4))^200 + abs(sin(4*u/4))^200)^(-1/200) sage: f_y = cos(u)*sin(v)*(abs(cos(3*v/4))^500 + abs(sin(3*v/4))^500)^(-1/260)*(abs(cos(4*u/4))^200 + abs(sin(4*u/4))^200)^(-1/200) sage: f_z = sin(u)*(abs(cos(4*u/4))^200 + abs(sin(4*u/4))^200)^(-1/200) sage: parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, 0, 2*pi)) Double heart:: sage: u, v = var('u,v') sage: f_x = ( abs(v) - abs(u) - abs(tanh((1/sqrt(2))*u)/(1/sqrt(2))) + abs(tanh((1/sqrt(2))*v)/(1/sqrt(2))) )*sin(v) sage: f_y = ( abs(v) - abs(u) - abs(tanh((1/sqrt(2))*u)/(1/sqrt(2))) - abs(tanh((1/sqrt(2))*v)/(1/sqrt(2))) )*cos(v) sage: f_z = sin(u)*(abs(cos(4*u/4))^1 + abs(sin(4*u/4))^1)^(-1/1) sage: parametric_plot3d([f_x, f_y, f_z], (u, 0, pi), (v, -pi, pi)) Heart:: sage: u, v = var('u,v') sage: f_x = cos(u)*(4*sqrt(1-v^2)*sin(abs(u))^abs(u)) sage: f_y = sin(u) *(4*sqrt(1-v^2)*sin(abs(u))^abs(u)) sage: f_z = v sage: parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, -1, 1), frame=False, color="red") Green bowtie:: sage: u, v = var('u,v') sage: f_x = sin(u) / (sqrt(2) + sin(v)) sage: f_y = sin(u) / (sqrt(2) + cos(v)) sage: f_z = cos(u) / (1 + sqrt(2)) sage: parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, -pi, pi), frame=False, color="green") Boy's surface http://en.wikipedia.org/wiki/Boy's_surface :: sage: u, v = var('u,v') sage: fx = 2/3* (cos(u)* cos(2*v) + sqrt(2)* sin(u)* cos(v))* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v)) sage: fy = 2/3* (cos(u)* sin(2*v) - sqrt(2)* sin(u)* sin(v))* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v)) sage: fz = sqrt(2)* cos(u)* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v)) sage: parametric_plot3d([fx, fy, fz], (u, -2*pi, 2*pi), (v, 0, pi), plot_points = [90,90], frame=False, color="orange") # long time -- about 30 seconds Maeder's_Owl (pretty but can't find an internet reference):: sage: u, v = var('u,v') sage: fx = v *cos(u) - 0.5* v^2 * cos(2* u) sage: fy = -v *sin(u) - 0.5* v^2 * sin(2* u) sage: fz = 4 *v^1.5 * cos(3 *u / 2) / 3 sage: parametric_plot3d([fx, fy, fz], (u, -2*pi, 2*pi), (v, 0, 1),plot_points = [90,90], frame=False, color="purple") Bracelet:: sage: u, v = var('u,v') sage: fx = (2 + 0.2*sin(2*pi*u))*sin(pi*v) sage: fy = 0.2*cos(2*pi*u) *3*cos(2*pi*v) sage: fz = (2 + 0.2*sin(2*pi*u))*cos(pi*v) sage: parametric_plot3d([fx, fy, fz], (u, 0, pi/2), (v, 0, 3*pi/4), frame=False, color="gray") Green goblet :: sage: u, v = var('u,v') sage: fx = cos(u)*cos(2*v) sage: fy = sin(u)*cos(2*v) sage: fz = sin(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, pi), frame=False, color="green") Funny folded surface - with square projection:: sage: u, v = var('u,v') sage: fx = cos(u)*sin(2*v) sage: fy = sin(u)*cos(2*v) sage: fz = sin(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="green") Surface of revolution of figure 8:: sage: u, v = var('u,v') sage: fx = cos(u)*sin(2*v) sage: fy = sin(u)*sin(2*v) sage: fz = sin(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="green") Yellow Whitney's umbrella http://en.wikipedia.org/wiki/Whitney_umbrella:: sage: u, v = var('u,v') sage: fx = u*v sage: fy = u sage: fz = v^2 sage: parametric_plot3d([fx, fy, fz], (u, -1, 1), (v, -1, 1), frame=False, color="yellow") Cross cap http://en.wikipedia.org/wiki/Cross-cap:: sage: u, v = var('u,v') sage: fx = (1+cos(v))*cos(u) sage: fy = (1+cos(v))*sin(u) sage: fz = -tanh((2/3)*(u-pi))*sin(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="red") Twisted torus:: sage: u, v = var('u,v') sage: fx = (3+sin(v)+cos(u))*cos(2*v) sage: fy = (3+sin(v)+cos(u))*sin(2*v) sage: fz = sin(u)+2*cos(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="red") Four intersecting discs:: sage: u, v = var('u,v') sage: fx = v *cos(u) -0.5*v^2*cos(2*u) sage: fy = -v*sin(u) -0.5*v^2*sin(2*u) sage: fz = 4* v^1.5 *cos(3* u / 2) / 3 sage: parametric_plot3d([fx, fy, fz], (u, 0, 4*pi), (v, 0,2*pi), frame=False, color="red", opacity=0.7) Steiner surface/Roman's surface (see http://en.wikipedia.org/wiki/Roman_surface and http://en.wikipedia.org/wiki/Steiner_surface):: sage: u, v = var('u,v') sage: fx = (sin(2 * u) * cos(v) * cos(v)) sage: fy = (sin(u) * sin(2 * v)) sage: fz = (cos(u) * sin(2 * v)) sage: parametric_plot3d([fx, fy, fz], (u, -pi/2, pi/2), (v, -pi/2,pi/2), frame=False, color="red") Klein bottle? (see http://en.wikipedia.org/wiki/Klein_bottle):: sage: u, v = var('u,v') sage: fx = (3*(1+sin(v)) + 2*(1-cos(v)/2)*cos(u))*cos(v) sage: fy = (4+2*(1-cos(v)/2)*cos(u))*sin(v) sage: fz = -2*(1-cos(v)/2) * sin(u) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="green") A Figure 8 embedding of the Klein bottle (see http://en.wikipedia.org/wiki/Klein_bottle):: sage: u, v = var('u,v') sage: fx = (2 + cos(v/2)* sin(u) - sin(v/2)* sin(2 *u))* cos(v) sage: fy = (2 + cos(v/2)* sin(u) - sin(v/2)* sin(2 *u))* sin(v) sage: fz = sin(v/2)* sin(u) + cos(v/2) *sin(2* u) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="red") Enneper's surface (see http://en.wikipedia.org/wiki/Enneper_surface):: sage: u, v = var('u,v') sage: fx = u -u^3/3 + u*v^2 sage: fy = v -v^3/3 + v*u^2 sage: fz = u^2 - v^2 sage: parametric_plot3d([fx, fy, fz], (u, -2, 2), (v, -2, 2), frame=False, color="red") Henneberg's surface (see http://xahlee.org/surface/gallery_m.html) :: sage: u, v = var('u,v') sage: fx = 2*sinh(u)*cos(v) -(2/3)*sinh(3*u)*cos(3*v) sage: fy = 2*sinh(u)*sin(v) +(2/3)*sinh(3*u)*sin(3*v) sage: fz = 2*cosh(2*u)*cos(2*v) sage: parametric_plot3d([fx, fy, fz], (u, -1, 1), (v, -pi/2, pi/2), frame=False, color="red") Dini's spiral :: sage: u, v = var('u,v') sage: fx = cos(u)*sin(v) sage: fy = sin(u)*sin(v) sage: fz = (cos(v)+log(tan(v/2))) + 0.2*u sage: parametric_plot3d([fx, fy, fz], (u, 0, 12.4), (v, 0.1, 2),frame=False, color="red") Catalan's surface (see http://xahlee.org/surface/catalan/catalan.html):: sage: u, v = var('u,v') sage: fx = u-sin(u)*cosh(v) sage: fy = 1-cos(u)*cosh(v) sage: fz = 4*sin(1/2*u)*sinh(v/2) sage: parametric_plot3d([fx, fy, fz], (u, -pi, 3*pi), (v, -2, 2), frame=False, color="red") A Conchoid:: sage: u, v = var('u,v') sage: k = 1.2; k_2 = 1.2; a = 1.5 sage: f = (k^u*(1+cos(v))*cos(u), k^u*(1+cos(v))*sin(u), k^u*sin(v)-a*k_2^u) sage: parametric_plot3d(f, (u,0,6*pi), (v,0,2*pi), plot_points=[40,40], texture=(0,0.5,0)) A Mobius strip:: sage: u,v = var("u,v") sage: parametric_plot3d([cos(u)*(1+v*cos(u/2)), sin(u)*(1+v*cos(u/2)), 0.2*v*sin(u/2)], (u,0, 4*pi+0.5), (v,0, 0.3),plot_points=[50,50]) A Twisted Ribbon :: sage: u, v = var('u,v') sage: parametric_plot3d([3*sin(u)*cos(v), 3*sin(u)*sin(v), cos(v)], (u,0, 2*pi), (v, 0, pi),plot_points=[50,50]) An Ellipsoid:: sage: u, v = var('u,v') sage: parametric_plot3d([3*sin(u)*cos(v), 2*sin(u)*sin(v), cos(u)], (u,0, 2*pi), (v, 0, 2*pi),plot_points=[50,50], aspect_ratio=[1,1,1]) A Cone:: sage: u, v = var('u,v') sage: parametric_plot3d([u*cos(v), u*sin(v), u], (u, -1, 1), (v, 0, 2*pi+0.5), plot_points=[50,50]) A Paraboloid:: sage: u, v = var('u,v') sage: parametric_plot3d([u*cos(v), u*sin(v), u^2], (u, 0, 1), (v, 0, 2*pi+0.4), plot_points=[50,50]) A Hyperboloid:: sage: u, v = var('u,v') sage: plot3d(u^2-v^2, (u, -1, 1), (v, -1, 1), plot_points=[50,50]) A weird looking surface - like a Mobius band but also an O:: sage: u, v = var('u,v') sage: parametric_plot3d([sin(u)*cos(u)*log(u^2)*sin(v), (u^2)^(1/6)*(cos(u)^2)^(1/4)*cos(v), sin(v)], (u, 0.001, 1), (v, -pi, pi+0.2), plot_points=[50,50]) A heart, but not a cardioid (for my wife):: sage: u, v = var('u,v') sage: p1 = parametric_plot3d([sin(u)*cos(u)*log(u^2)*v*(1-v)/2, ((u^6)^(1/20)*(cos(u)^2)^(1/4)-1/2)*v*(1-v), v^(0.5)], (u, 0.001, 1), (v, 0, 1), plot_points=[70,70], color='red') sage: p2 = parametric_plot3d([-sin(u)*cos(u)*log(u^2)*v*(1-v)/2, ((u^6)^(1/20)*(cos(u)^2)^(1/4)-1/2)*v*(1-v), v^(0.5)], (u, 0.001, 1), (v, 0, 1), plot_points=[70,70], color='red') sage: show(p1+p2, frame=False) A Hyperhelicoidal:: sage: u = var("u") sage: v = var("v") sage: fx = (sinh(v)*cos(3*u))/(1+cosh(u)*cosh(v)) sage: fy = (sinh(v)*sin(3*u))/(1+cosh(u)*cosh(v)) sage: fz = (cosh(v)*sinh(u))/(1+cosh(u)*cosh(v)) sage: parametric_plot3d([fx, fy, fz], (u, -pi, pi), (v, -pi, pi), plot_points = [50,50], frame=False, color="red") A Helicoid (lines through a helix, http://en.wikipedia.org/wiki/Helix):: sage: u, v = var('u,v') sage: fx = sinh(v)*sin(u) sage: fy = -sinh(v)*cos(u) sage: fz = 3*u sage: parametric_plot3d([fx, fy, fz], (u, -pi, pi), (v, -pi, pi), plot_points = [50,50], frame=False, color="red") Kuen's surface (http://www.math.umd.edu/research/bianchi/Gifccsurfs/ccsurfs.html):: sage: fx = (2*(cos(u) + u*sin(u))*sin(v))/(1+ u^2*sin(v)^2) sage: fy = (2*(sin(u) - u*cos(u))*sin(v))/(1+ u^2*sin(v)^2) sage: fz = log(tan(1/2 *v)) + (2*cos(v))/(1+ u^2*sin(v)^2) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0.01, pi-0.01), plot_points = [50,50], frame=False, color="green") A 5-pointed star:: sage: fx = cos(u)*cos(v)*(abs(cos(1*u/4))^0.5 + abs(sin(1*u/4))^0.5)^(-1/0.3)*(abs(cos(5*v/4))^1.7 + abs(sin(5*v/4))^1.7)^(-1/0.1) sage: fy = cos(u)*sin(v)*(abs(cos(1*u/4))^0.5 + abs(sin(1*u/4))^0.5)^(-1/0.3)*(abs(cos(5*v/4))^1.7 + abs(sin(5*v/4))^1.7)^(-1/0.1) sage: fz = sin(u)*(abs(cos(1*u/4))^0.5 + abs(sin(1*u/4))^0.5)^(-1/0.3) sage: parametric_plot3d([fx, fy, fz], (u, -pi/2, pi/2), (v, 0, 2*pi), plot_points = [50,50], frame=False, color="green") A cool self-intersecting surface (Eppener surface?):: sage: fx = u - u^3/3 + u*v^2 sage: fy = v - v^3/3 + v*u^2 sage: fz = u^2 - v^2 sage: parametric_plot3d([fx, fy, fz], (u, -25, 25), (v, -25, 25), plot_points = [50,50], frame=False, color="green") The breather surface (http://en.wikipedia.org/wiki/Breather_surface):: sage: fx = (2*sqrt(0.84)*cosh(0.4*u)*(-(sqrt(0.84)*cos(v)*cos(sqrt(0.84)*v)) - sin(v)*sin(sqrt(0.84)*v)))/(0.4*((sqrt(0.84)*cosh(0.4*u))^2 + (0.4*sin(sqrt(0.84)*v))^2)) sage: fy = (2*sqrt(0.84)*cosh(0.4*u)*(-(sqrt(0.84)*sin(v)*cos(sqrt(0.84)*v)) + cos(v)*sin(sqrt(0.84)*v)))/(0.4*((sqrt(0.84)*cosh(0.4*u))^2 + (0.4*sin(sqrt(0.84)*v))^2)) sage: fz = -u + (2*0.84*cosh(0.4*u)*sinh(0.4*u))/(0.4*((sqrt(0.84)*cosh(0.4*u))^2 + (0.4*sin(sqrt(0.84)*v))^2)) sage: parametric_plot3d([fx, fy, fz], (u, -13.2, 13.2), (v, -37.4, 37.4), plot_points = [90,90], frame=False, color="green") TESTS:: sage: u, v = var('u,v') sage: plot3d(u^2-v^2, (u, -1, 1), (u, -1, 1)) Traceback (most recent call last): ... ValueError: range variables should be distinct, but there are duplicates From Trac #2858:: sage: parametric_plot3d((u,-u,v), (u,-10,10),(v,-10,10)) sage: f(u)=u; g(v)=v^2; parametric_plot3d((g,f,f), (-10,10),(-10,10)) From Trac #5368:: sage: x, y = var('x,y') sage: plot3d(x*y^2 - sin(x), (x,-1,1), (y,-1,1)) """ # TODO: # * Surface -- behavior of functions not defined everywhere -- see note above # * Iterative refinement # color_function -- (default: "automatic") how to determine the color of curves and surfaces # color_function_scaling -- (default: True) whether to scale the input to color_function # exclusions -- (default: "automatic") u points or (u,v) conditions to exclude. # (E.g., exclusions could be a function e = lambda u, v: False if u < v else True # exclusions_style -- (default: None) what to draw at excluded points # max_recursion -- (default: "automatic") maximum number of recursive subdivisions, # when ... # mesh -- (default: "automatic") how many mesh divisions in each direction to draw # mesh_functions -- (default: "automatic") how to determine the placement of mesh divisions # mesh_shading -- (default: None) how to shade regions between mesh divisions # plot_range -- (default: "automatic") range of values to include if is_Vector(f): f = tuple(f) if isinstance(f, (list,tuple)) and len(f) > 0 and isinstance(f[0], (list,tuple)): return sum([parametric_plot3d(v, urange, vrange, plot_points=plot_points, **kwds) for v in f]) if not isinstance(f, (tuple, list)) or len(f) != 3: raise ValueError, "f must be a list, tuple, or vector of length 3" if vrange is None: if plot_points == "automatic": plot_points = 75 G = _parametric_plot3d_curve(f, urange, plot_points=plot_points, **kwds) else: if plot_points == "automatic": plot_points = [40,40] G = _parametric_plot3d_surface(f, urange, vrange, plot_points=plot_points, boundary_style=boundary_style, **kwds) G._set_extra_kwds(kwds) return G
def parametric_plot3d(f, urange, vrange=None, plot_points="automatic", boundary_style=None, **kwds): r""" Return a parametric three-dimensional space curve or surface. There are four ways to call this function: - ``parametric_plot3d([f_x, f_y, f_z], (u_min, u_max))``: `f_x, f_y, f_z` are three functions and `u_{\min}` and `u_{\max}` are real numbers - ``parametric_plot3d([f_x, f_y, f_z], (u, u_min, u_max))``: `f_x, f_y, f_z` can be viewed as functions of `u` - ``parametric_plot3d([f_x, f_y, f_z], (u_min, u_max), (v_min, v_max))``: `f_x, f_y, f_z` are each functions of two variables - ``parametric_plot3d([f_x, f_y, f_z], (u, u_min, u_max), (v, v_min, v_max))``: `f_x, f_y, f_z` can be viewed as functions of `u` and `v` INPUT: - ``f`` - a 3-tuple of functions or expressions, or vector of size 3 - ``urange`` - a 2-tuple (u_min, u_max) or a 3-tuple (u, u_min, u_max) - ``vrange`` - (optional - only used for surfaces) a 2-tuple (v_min, v_max) or a 3-tuple (v, v_min, v_max) - ``plot_points`` - (default: "automatic", which is 75 for curves and [40,40] for surfaces) initial number of sample points in each parameter; an integer for a curve, and a pair of integers for a surface. - ``boundary_style`` - (default: None, no boundary) a dict that describes how to draw the boundaries of regions by giving options that are passed to the line3d command. - ``mesh`` - bool (default: False) whether to display mesh grid lines - ``dots`` - bool (default: False) whether to display dots at mesh grid points .. note:: #. By default for a curve any points where `f_x`, `f_y`, or `f_z` do not evaluate to a real number are skipped. #. Currently for a surface `f_x`, `f_y`, and `f_z` have to be defined everywhere. This will change. #. mesh and dots are not supported when using the Tachyon ray tracer renderer. EXAMPLES: We demonstrate each of the four ways to call this function. #. A space curve defined by three functions of 1 variable: :: sage: parametric_plot3d( (sin, cos, lambda u: u/10), (0, 20)) Note above the lambda function, which creates a callable Python function that sends `u` to `u/10`. #. Next we draw the same plot as above, but using symbolic functions: :: sage: u = var('u') sage: parametric_plot3d( (sin(u), cos(u), u/10), (u, 0, 20)) #. We draw a parametric surface using 3 Python functions (defined using lambda): :: sage: f = (lambda u,v: cos(u), lambda u,v: sin(u)+cos(v), lambda u,v: sin(v)) sage: parametric_plot3d(f, (0, 2*pi), (-pi, pi)) #. The surface, but with a mesh: :: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u) + cos(v), sin(v)), (u, 0, 2*pi), (v, -pi, pi), mesh=True) #. The same surface, but where the defining functions are symbolic: :: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u) + cos(v), sin(v)), (u, 0, 2*pi), (v, -pi, pi)) We increase the number of plot points, and make the surface green and transparent: :: sage: parametric_plot3d((cos(u), sin(u) + cos(v), sin(v)), (u, 0, 2*pi), (v, -pi, pi), color='green', opacity=0.1, plot_points=[30,30]) We call the space curve function but with polynomials instead of symbolic variables. :: sage: R.<t> = RDF[] sage: parametric_plot3d( (t, t^2, t^3), (t, 0, 3) ) Next we plot the same curve, but because we use (0, 3) instead of (t, 0, 3), each polynomial is viewed as a callable function of one variable:: sage: parametric_plot3d( (t, t^2, t^3), (0, 3) ) We do a plot but mix a symbolic input, and an integer:: sage: t = var('t') sage: parametric_plot3d( (1, sin(t), cos(t)), (t, 0, 3) ) We specify a boundary style to show us the values of the function at its extrema:: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u) + cos(v), sin(v)), (u, 0, pi), (v, 0, pi), \ ... boundary_style={"color": "black", "thickness": 2}) We can plot vectors:: sage: x,y=var('x,y') sage: parametric_plot3d(vector([x-y,x*y,x*cos(y)]), (x,0,2), (y,0,2)) sage: t=var('t') sage: p=vector([1,2,3]) sage: q=vector([2,-1,2]) sage: parametric_plot3d(p*t+q, (t, 0, 2)) Any options you would normally use to specify the appearance of a curve are valid as entries in the boundary_style dict. MANY MORE EXAMPLES: We plot two interlinked tori:: sage: u, v = var('u,v') sage: f1 = (4+(3+cos(v))*sin(u), 4+(3+cos(v))*cos(u), 4+sin(v)) sage: f2 = (8+(3+cos(v))*cos(u), 3+sin(v), 4+(3+cos(v))*sin(u)) sage: p1 = parametric_plot3d(f1, (u,0,2*pi), (v,0,2*pi), texture="red") sage: p2 = parametric_plot3d(f2, (u,0,2*pi), (v,0,2*pi), texture="blue") sage: p1 + p2 A cylindrical Star of David:: sage: u,v = var('u v') sage: f_x = cos(u)*cos(v)*(abs(cos(3*v/4))^500 + abs(sin(3*v/4))^500)^(-1/260)*(abs(cos(4*u/4))^200 + abs(sin(4*u/4))^200)^(-1/200) sage: f_y = cos(u)*sin(v)*(abs(cos(3*v/4))^500 + abs(sin(3*v/4))^500)^(-1/260)*(abs(cos(4*u/4))^200 + abs(sin(4*u/4))^200)^(-1/200) sage: f_z = sin(u)*(abs(cos(4*u/4))^200 + abs(sin(4*u/4))^200)^(-1/200) sage: parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, 0, 2*pi)) Double heart:: sage: u, v = var('u,v') sage: f_x = ( abs(v) - abs(u) - abs(tanh((1/sqrt(2))*u)/(1/sqrt(2))) + abs(tanh((1/sqrt(2))*v)/(1/sqrt(2))) )*sin(v) sage: f_y = ( abs(v) - abs(u) - abs(tanh((1/sqrt(2))*u)/(1/sqrt(2))) - abs(tanh((1/sqrt(2))*v)/(1/sqrt(2))) )*cos(v) sage: f_z = sin(u)*(abs(cos(4*u/4))^1 + abs(sin(4*u/4))^1)^(-1/1) sage: parametric_plot3d([f_x, f_y, f_z], (u, 0, pi), (v, -pi, pi)) Heart:: sage: u, v = var('u,v') sage: f_x = cos(u)*(4*sqrt(1-v^2)*sin(abs(u))^abs(u)) sage: f_y = sin(u) *(4*sqrt(1-v^2)*sin(abs(u))^abs(u)) sage: f_z = v sage: parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, -1, 1), frame=False, color="red") Green bowtie:: sage: u, v = var('u,v') sage: f_x = sin(u) / (sqrt(2) + sin(v)) sage: f_y = sin(u) / (sqrt(2) + cos(v)) sage: f_z = cos(u) / (1 + sqrt(2)) sage: parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, -pi, pi), frame=False, color="green") Boy's surface http://en.wikipedia.org/wiki/Boy's_surface :: sage: u, v = var('u,v') sage: fx = 2/3* (cos(u)* cos(2*v) + sqrt(2)* sin(u)* cos(v))* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v)) sage: fy = 2/3* (cos(u)* sin(2*v) - sqrt(2)* sin(u)* sin(v))* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v)) sage: fz = sqrt(2)* cos(u)* cos(u) / (sqrt(2) - sin(2*u)* sin(3*v)) sage: parametric_plot3d([fx, fy, fz], (u, -2*pi, 2*pi), (v, 0, pi), plot_points = [90,90], frame=False, color="orange") # long time -- about 30 seconds Maeder's_Owl (pretty but can't find an internet reference):: sage: u, v = var('u,v') sage: fx = v *cos(u) - 0.5* v^2 * cos(2* u) sage: fy = -v *sin(u) - 0.5* v^2 * sin(2* u) sage: fz = 4 *v^1.5 * cos(3 *u / 2) / 3 sage: parametric_plot3d([fx, fy, fz], (u, -2*pi, 2*pi), (v, 0, 1),plot_points = [90,90], frame=False, color="purple") Bracelet:: sage: u, v = var('u,v') sage: fx = (2 + 0.2*sin(2*pi*u))*sin(pi*v) sage: fy = 0.2*cos(2*pi*u) *3*cos(2*pi*v) sage: fz = (2 + 0.2*sin(2*pi*u))*cos(pi*v) sage: parametric_plot3d([fx, fy, fz], (u, 0, pi/2), (v, 0, 3*pi/4), frame=False, color="gray") Green goblet :: sage: u, v = var('u,v') sage: fx = cos(u)*cos(2*v) sage: fy = sin(u)*cos(2*v) sage: fz = sin(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, pi), frame=False, color="green") Funny folded surface - with square projection:: sage: u, v = var('u,v') sage: fx = cos(u)*sin(2*v) sage: fy = sin(u)*cos(2*v) sage: fz = sin(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="green") Surface of revolution of figure 8:: sage: u, v = var('u,v') sage: fx = cos(u)*sin(2*v) sage: fy = sin(u)*sin(2*v) sage: fz = sin(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="green") Yellow Whitney's umbrella http://en.wikipedia.org/wiki/Whitney_umbrella:: sage: u, v = var('u,v') sage: fx = u*v sage: fy = u sage: fz = v^2 sage: parametric_plot3d([fx, fy, fz], (u, -1, 1), (v, -1, 1), frame=False, color="yellow") Cross cap http://en.wikipedia.org/wiki/Cross-cap:: sage: u, v = var('u,v') sage: fx = (1+cos(v))*cos(u) sage: fy = (1+cos(v))*sin(u) sage: fz = -tanh((2/3)*(u-pi))*sin(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="red") Twisted torus:: sage: u, v = var('u,v') sage: fx = (3+sin(v)+cos(u))*cos(2*v) sage: fy = (3+sin(v)+cos(u))*sin(2*v) sage: fz = sin(u)+2*cos(v) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="red") Four intersecting discs:: sage: u, v = var('u,v') sage: fx = v *cos(u) -0.5*v^2*cos(2*u) sage: fy = -v*sin(u) -0.5*v^2*sin(2*u) sage: fz = 4* v^1.5 *cos(3* u / 2) / 3 sage: parametric_plot3d([fx, fy, fz], (u, 0, 4*pi), (v, 0,2*pi), frame=False, color="red", opacity=0.7) Steiner surface/Roman's surface (see http://en.wikipedia.org/wiki/Roman_surface and http://en.wikipedia.org/wiki/Steiner_surface):: sage: u, v = var('u,v') sage: fx = (sin(2 * u) * cos(v) * cos(v)) sage: fy = (sin(u) * sin(2 * v)) sage: fz = (cos(u) * sin(2 * v)) sage: parametric_plot3d([fx, fy, fz], (u, -pi/2, pi/2), (v, -pi/2,pi/2), frame=False, color="red") Klein bottle? (see http://en.wikipedia.org/wiki/Klein_bottle):: sage: u, v = var('u,v') sage: fx = (3*(1+sin(v)) + 2*(1-cos(v)/2)*cos(u))*cos(v) sage: fy = (4+2*(1-cos(v)/2)*cos(u))*sin(v) sage: fz = -2*(1-cos(v)/2) * sin(u) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="green") A Figure 8 embedding of the Klein bottle (see http://en.wikipedia.org/wiki/Klein_bottle):: sage: u, v = var('u,v') sage: fx = (2 + cos(v/2)* sin(u) - sin(v/2)* sin(2 *u))* cos(v) sage: fy = (2 + cos(v/2)* sin(u) - sin(v/2)* sin(2 *u))* sin(v) sage: fz = sin(v/2)* sin(u) + cos(v/2) *sin(2* u) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0, 2*pi), frame=False, color="red") Enneper's surface (see http://en.wikipedia.org/wiki/Enneper_surface):: sage: u, v = var('u,v') sage: fx = u -u^3/3 + u*v^2 sage: fy = v -v^3/3 + v*u^2 sage: fz = u^2 - v^2 sage: parametric_plot3d([fx, fy, fz], (u, -2, 2), (v, -2, 2), frame=False, color="red") Henneberg's surface (see http://xahlee.org/surface/gallery_m.html) :: sage: u, v = var('u,v') sage: fx = 2*sinh(u)*cos(v) -(2/3)*sinh(3*u)*cos(3*v) sage: fy = 2*sinh(u)*sin(v) +(2/3)*sinh(3*u)*sin(3*v) sage: fz = 2*cosh(2*u)*cos(2*v) sage: parametric_plot3d([fx, fy, fz], (u, -1, 1), (v, -pi/2, pi/2), frame=False, color="red") Dini's spiral :: sage: u, v = var('u,v') sage: fx = cos(u)*sin(v) sage: fy = sin(u)*sin(v) sage: fz = (cos(v)+log(tan(v/2))) + 0.2*u sage: parametric_plot3d([fx, fy, fz], (u, 0, 12.4), (v, 0.1, 2),frame=False, color="red") Catalan's surface (see http://xahlee.org/surface/catalan/catalan.html):: sage: u, v = var('u,v') sage: fx = u-sin(u)*cosh(v) sage: fy = 1-cos(u)*cosh(v) sage: fz = 4*sin(1/2*u)*sinh(v/2) sage: parametric_plot3d([fx, fy, fz], (u, -pi, 3*pi), (v, -2, 2), frame=False, color="red") A Conchoid:: sage: u, v = var('u,v') sage: k = 1.2; k_2 = 1.2; a = 1.5 sage: f = (k^u*(1+cos(v))*cos(u), k^u*(1+cos(v))*sin(u), k^u*sin(v)-a*k_2^u) sage: parametric_plot3d(f, (u,0,6*pi), (v,0,2*pi), plot_points=[40,40], texture=(0,0.5,0)) A Mobius strip:: sage: u,v = var("u,v") sage: parametric_plot3d([cos(u)*(1+v*cos(u/2)), sin(u)*(1+v*cos(u/2)), 0.2*v*sin(u/2)], (u,0, 4*pi+0.5), (v,0, 0.3),plot_points=[50,50]) A Twisted Ribbon :: sage: u, v = var('u,v') sage: parametric_plot3d([3*sin(u)*cos(v), 3*sin(u)*sin(v), cos(v)], (u,0, 2*pi), (v, 0, pi),plot_points=[50,50]) An Ellipsoid:: sage: u, v = var('u,v') sage: parametric_plot3d([3*sin(u)*cos(v), 2*sin(u)*sin(v), cos(u)], (u,0, 2*pi), (v, 0, 2*pi),plot_points=[50,50], aspect_ratio=[1,1,1]) A Cone:: sage: u, v = var('u,v') sage: parametric_plot3d([u*cos(v), u*sin(v), u], (u, -1, 1), (v, 0, 2*pi+0.5), plot_points=[50,50]) A Paraboloid:: sage: u, v = var('u,v') sage: parametric_plot3d([u*cos(v), u*sin(v), u^2], (u, 0, 1), (v, 0, 2*pi+0.4), plot_points=[50,50]) A Hyperboloid:: sage: u, v = var('u,v') sage: plot3d(u^2-v^2, (u, -1, 1), (v, -1, 1), plot_points=[50,50]) A weird looking surface - like a Mobius band but also an O:: sage: u, v = var('u,v') sage: parametric_plot3d([sin(u)*cos(u)*log(u^2)*sin(v), (u^2)^(1/6)*(cos(u)^2)^(1/4)*cos(v), sin(v)], (u, 0.001, 1), (v, -pi, pi+0.2), plot_points=[50,50]) A heart, but not a cardioid (for my wife):: sage: u, v = var('u,v') sage: p1 = parametric_plot3d([sin(u)*cos(u)*log(u^2)*v*(1-v)/2, ((u^6)^(1/20)*(cos(u)^2)^(1/4)-1/2)*v*(1-v), v^(0.5)], (u, 0.001, 1), (v, 0, 1), plot_points=[70,70], color='red') sage: p2 = parametric_plot3d([-sin(u)*cos(u)*log(u^2)*v*(1-v)/2, ((u^6)^(1/20)*(cos(u)^2)^(1/4)-1/2)*v*(1-v), v^(0.5)], (u, 0.001, 1), (v, 0, 1), plot_points=[70,70], color='red') sage: show(p1+p2, frame=False) A Hyperhelicoidal:: sage: u = var("u") sage: v = var("v") sage: fx = (sinh(v)*cos(3*u))/(1+cosh(u)*cosh(v)) sage: fy = (sinh(v)*sin(3*u))/(1+cosh(u)*cosh(v)) sage: fz = (cosh(v)*sinh(u))/(1+cosh(u)*cosh(v)) sage: parametric_plot3d([fx, fy, fz], (u, -pi, pi), (v, -pi, pi), plot_points = [50,50], frame=False, color="red") A Helicoid (lines through a helix, http://en.wikipedia.org/wiki/Helix):: sage: u, v = var('u,v') sage: fx = sinh(v)*sin(u) sage: fy = -sinh(v)*cos(u) sage: fz = 3*u sage: parametric_plot3d([fx, fy, fz], (u, -pi, pi), (v, -pi, pi), plot_points = [50,50], frame=False, color="red") Kuen's surface (http://virtualmathmuseum.org/Surface/kuen/kuen.html):: sage: fx = (2*(cos(u) + u*sin(u))*sin(v))/(1+ u^2*sin(v)^2) sage: fy = (2*(sin(u) - u*cos(u))*sin(v))/(1+ u^2*sin(v)^2) sage: fz = log(tan(1/2 *v)) + (2*cos(v))/(1+ u^2*sin(v)^2) sage: parametric_plot3d([fx, fy, fz], (u, 0, 2*pi), (v, 0.01, pi-0.01), plot_points = [50,50], frame=False, color="green") A 5-pointed star:: sage: fx = cos(u)*cos(v)*(abs(cos(1*u/4))^0.5 + abs(sin(1*u/4))^0.5)^(-1/0.3)*(abs(cos(5*v/4))^1.7 + abs(sin(5*v/4))^1.7)^(-1/0.1) sage: fy = cos(u)*sin(v)*(abs(cos(1*u/4))^0.5 + abs(sin(1*u/4))^0.5)^(-1/0.3)*(abs(cos(5*v/4))^1.7 + abs(sin(5*v/4))^1.7)^(-1/0.1) sage: fz = sin(u)*(abs(cos(1*u/4))^0.5 + abs(sin(1*u/4))^0.5)^(-1/0.3) sage: parametric_plot3d([fx, fy, fz], (u, -pi/2, pi/2), (v, 0, 2*pi), plot_points = [50,50], frame=False, color="green") A cool self-intersecting surface (Eppener surface?):: sage: fx = u - u^3/3 + u*v^2 sage: fy = v - v^3/3 + v*u^2 sage: fz = u^2 - v^2 sage: parametric_plot3d([fx, fy, fz], (u, -25, 25), (v, -25, 25), plot_points = [50,50], frame=False, color="green") The breather surface (http://en.wikipedia.org/wiki/Breather_surface):: sage: fx = (2*sqrt(0.84)*cosh(0.4*u)*(-(sqrt(0.84)*cos(v)*cos(sqrt(0.84)*v)) - sin(v)*sin(sqrt(0.84)*v)))/(0.4*((sqrt(0.84)*cosh(0.4*u))^2 + (0.4*sin(sqrt(0.84)*v))^2)) sage: fy = (2*sqrt(0.84)*cosh(0.4*u)*(-(sqrt(0.84)*sin(v)*cos(sqrt(0.84)*v)) + cos(v)*sin(sqrt(0.84)*v)))/(0.4*((sqrt(0.84)*cosh(0.4*u))^2 + (0.4*sin(sqrt(0.84)*v))^2)) sage: fz = -u + (2*0.84*cosh(0.4*u)*sinh(0.4*u))/(0.4*((sqrt(0.84)*cosh(0.4*u))^2 + (0.4*sin(sqrt(0.84)*v))^2)) sage: parametric_plot3d([fx, fy, fz], (u, -13.2, 13.2), (v, -37.4, 37.4), plot_points = [90,90], frame=False, color="green") TESTS:: sage: u, v = var('u,v') sage: plot3d(u^2-v^2, (u, -1, 1), (u, -1, 1)) Traceback (most recent call last): ... ValueError: range variables should be distinct, but there are duplicates From Trac #2858:: sage: parametric_plot3d((u,-u,v), (u,-10,10),(v,-10,10)) sage: f(u)=u; g(v)=v^2; parametric_plot3d((g,f,f), (-10,10),(-10,10)) From Trac #5368:: sage: x, y = var('x,y') sage: plot3d(x*y^2 - sin(x), (x,-1,1), (y,-1,1)) """ # TODO: # * Surface -- behavior of functions not defined everywhere -- see note above # * Iterative refinement # color_function -- (default: "automatic") how to determine the color of curves and surfaces # color_function_scaling -- (default: True) whether to scale the input to color_function # exclusions -- (default: "automatic") u points or (u,v) conditions to exclude. # (E.g., exclusions could be a function e = lambda u, v: False if u < v else True # exclusions_style -- (default: None) what to draw at excluded points # max_recursion -- (default: "automatic") maximum number of recursive subdivisions, # when ... # mesh -- (default: "automatic") how many mesh divisions in each direction to draw # mesh_functions -- (default: "automatic") how to determine the placement of mesh divisions # mesh_shading -- (default: None) how to shade regions between mesh divisions # plot_range -- (default: "automatic") range of values to include if is_Vector(f): f = tuple(f) if isinstance(f, (list, tuple)) and len(f) > 0 and isinstance( f[0], (list, tuple)): return sum([ parametric_plot3d(v, urange, vrange, plot_points=plot_points, **kwds) for v in f ]) if not isinstance(f, (tuple, list)) or len(f) != 3: raise ValueError("f must be a list, tuple, or vector of length 3") if vrange is None: if plot_points == "automatic": plot_points = 75 G = _parametric_plot3d_curve(f, urange, plot_points=plot_points, **kwds) else: if plot_points == "automatic": plot_points = [40, 40] G = _parametric_plot3d_surface(f, urange, vrange, plot_points=plot_points, boundary_style=boundary_style, **kwds) G._set_extra_kwds(kwds) return G
def parametric_plot3d(f, urange, vrange=None, plot_points="automatic", boundary_style=None, **kwds): r""" Return a parametric three-dimensional space curve or surface. There are four ways to call this function: - ``parametric_plot3d([f_x, f_y, f_z], (u_min, u_max))``: `f_x, f_y, f_z` are three functions and `u_{\min}` and `u_{\max}` are real numbers - ``parametric_plot3d([f_x, f_y, f_z], (u, u_min, u_max))``: `f_x, f_y, f_z` can be viewed as functions of `u` - ``parametric_plot3d([f_x, f_y, f_z], (u_min, u_max), (v_min, v_max))``: `f_x, f_y, f_z` are each functions of two variables - ``parametric_plot3d([f_x, f_y, f_z], (u, u_min, u_max), (v, v_min, v_max))``: `f_x, f_y, f_z` can be viewed as functions of `u` and `v` INPUT: - ``f`` - a 3-tuple of functions or expressions, or vector of size 3 - ``urange`` - a 2-tuple (u_min, u_max) or a 3-tuple (u, u_min, u_max) - ``vrange`` - (optional - only used for surfaces) a 2-tuple (v_min, v_max) or a 3-tuple (v, v_min, v_max) - ``plot_points`` - (default: "automatic", which is 75 for curves and [40,40] for surfaces) initial number of sample points in each parameter; an integer for a curve, and a pair of integers for a surface. - ``boundary_style`` - (default: None, no boundary) a dict that describes how to draw the boundaries of regions by giving options that are passed to the line3d command. - ``mesh`` - bool (default: False) whether to display mesh grid lines - ``dots`` - bool (default: False) whether to display dots at mesh grid points .. note:: #. By default for a curve any points where `f_x`, `f_y`, or `f_z` do not evaluate to a real number are skipped. #. Currently for a surface `f_x`, `f_y`, and `f_z` have to be defined everywhere. This will change. #. mesh and dots are not supported when using the Tachyon ray tracer renderer. EXAMPLES: We demonstrate each of the four ways to call this function. #. A space curve defined by three functions of 1 variable: :: sage: parametric_plot3d((sin, cos, lambda u: u/10), (0,20)) Graphics3d Object .. PLOT:: sphinx_plot(parametric_plot3d((sin, cos, lambda u: u/10), (0,20))) Note above the lambda function, which creates a callable Python function that sends `u` to `u/10`. #. Next we draw the same plot as above, but using symbolic functions: :: sage: u = var('u') sage: parametric_plot3d((sin(u), cos(u), u/10), (u,0,20)) Graphics3d Object .. PLOT:: u = var('u') sphinx_plot(parametric_plot3d((sin(u), cos(u), u/10), (u,0,20))) #. We draw a parametric surface using 3 Python functions (defined using lambda): :: sage: f = (lambda u,v: cos(u), lambda u,v: sin(u)+cos(v), lambda u,v: sin(v)) sage: parametric_plot3d(f, (0,2*pi), (-pi,pi)) Graphics3d Object .. PLOT:: f = (lambda u,v: cos(u), lambda u,v: sin(u)+cos(v), lambda u,v: sin(v)) sphinx_plot(parametric_plot3d(f, (0,2*pi), (-pi,pi))) #. The same surface, but where the defining functions are symbolic: :: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi)) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d((cos(u), sin(u)+cos(v) ,sin(v)), (u,0,2*pi), (v,-pi,pi))) The surface, but with a mesh:: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), mesh=True) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), mesh=True)) We increase the number of plot points, and make the surface green and transparent:: sage: parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), ....: color='green', opacity=0.1, plot_points=[30,30]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), color='green', opacity=0.1, plot_points=[30,30])) One can also color the surface using a coloring function and a colormap:: sage: u,v = var('u,v') sage: def cf(u,v): return sin(u+v/2)**2 sage: P = parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), ....: (u,0,2*pi), (v,-pi,pi), color=(cf,colormaps.PiYG), plot_points=[60,60]) sage: P.show(viewer='tachyon') .. PLOT:: u,v = var('u,v') def cf(u,v): return sin(u+v/2)**2 P = parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), color=(cf,colormaps.PiYG), plot_points=[60,60]) sphinx_plot(P) Another example, a colored Möbius band:: sage: cm = colormaps.ocean sage: def c(x,y): return sin(x*y)**2 sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip sage: MoebiusStrip(5, 1, plot_points=200, color=(c,cm)) Graphics3d Object .. PLOT:: cm = colormaps.ocean def c(x,y): return sin(x*y)**2 from sage.plot.plot3d.parametric_surface import MoebiusStrip sphinx_plot(MoebiusStrip(5, 1, plot_points=200, color=(c,cm))) Yet another colored example:: sage: from sage.plot.plot3d.parametric_surface import ParametricSurface sage: cm = colormaps.autumn sage: def c(x,y): return sin(x*y)**2 sage: def g(x,y): return x, y+sin(y), x**2 + y**2 sage: ParametricSurface(g, (srange(-10,10,0.1), srange(-5,5.0,0.1)), color=(c,cm)) Graphics3d Object .. PLOT:: from sage.plot.plot3d.parametric_surface import ParametricSurface cm = colormaps.autumn def c(x,y): return sin(x*y)**2 def g(x,y): return x, y+sin(y), x**2 + y**2 sphinx_plot(ParametricSurface(g, (srange(-10,10,0.1), srange(-5,5.0,0.1)), color=(c,cm))) .. 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). We call the space curve function but with polynomials instead of symbolic variables. :: sage: R.<t> = RDF[] sage: parametric_plot3d((t, t^2, t^3), (t,0,3)) Graphics3d Object .. PLOT:: t = var('t') R = RDF['t'] sphinx_plot(parametric_plot3d((t, t**2, t**3), (t,0,3))) Next we plot the same curve, but because we use (0, 3) instead of (t, 0, 3), each polynomial is viewed as a callable function of one variable:: sage: parametric_plot3d((t, t^2, t^3), (0,3)) Graphics3d Object .. PLOT:: t = var('t') R = RDF['t'] sphinx_plot(parametric_plot3d((t, t**2, t**3), (0,3))) We do a plot but mix a symbolic input, and an integer:: sage: t = var('t') sage: parametric_plot3d((1, sin(t), cos(t)), (t,0,3)) Graphics3d Object .. PLOT:: t = var('t') sphinx_plot(parametric_plot3d((1, sin(t), cos(t)), (t,0,3))) We specify a boundary style to show us the values of the function at its extrema:: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,pi), (v,0,pi), ....: boundary_style={"color": "black", "thickness": 2}) Graphics3d Object .. PLOT:: u, v = var('u,v') P = parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,pi), (v,0,pi), boundary_style={"color":"black", "thickness":2}) sphinx_plot(P) We can plot vectors:: sage: x,y = var('x,y') sage: parametric_plot3d(vector([x-y, x*y, x*cos(y)]), (x,0,2), (y,0,2)) Graphics3d Object .. PLOT:: x,y = var('x,y') sphinx_plot(parametric_plot3d(vector([x-y, x*y, x*cos(y)]), (x,0,2), (y,0,2))) :: sage: t = var('t') sage: p = vector([1,2,3]) sage: q = vector([2,-1,2]) sage: parametric_plot3d(p*t+q, (t,0,2)) Graphics3d Object .. PLOT:: t = var('t') p = vector([1,2,3]) q = vector([2,-1,2]) sphinx_plot(parametric_plot3d(p*t+q, (t,0,2))) Any options you would normally use to specify the appearance of a curve are valid as entries in the ``boundary_style`` dict. MANY MORE EXAMPLES: We plot two interlinked tori:: sage: u, v = var('u,v') sage: f1 = (4+(3+cos(v))*sin(u), 4+(3+cos(v))*cos(u), 4+sin(v)) sage: f2 = (8+(3+cos(v))*cos(u), 3+sin(v), 4+(3+cos(v))*sin(u)) sage: p1 = parametric_plot3d(f1, (u,0,2*pi), (v,0,2*pi), texture="red") sage: p2 = parametric_plot3d(f2, (u,0,2*pi), (v,0,2*pi), texture="blue") sage: p1 + p2 Graphics3d Object .. PLOT:: u, v = var('u,v') f1 = (4+(3+cos(v))*sin(u), 4+(3+cos(v))*cos(u), 4+sin(v)) f2 = (8+(3+cos(v))*cos(u), 3+sin(v), 4+(3+cos(v))*sin(u)) p1 = parametric_plot3d(f1, (u,0,2*pi), (v,0,2*pi), texture="red") p2 = parametric_plot3d(f2, (u,0,2*pi), (v,0,2*pi), texture="blue") sphinx_plot(p1 + p2) A cylindrical Star of David:: sage: u,v = var('u v') sage: K = (abs(cos(u))^200+abs(sin(u))^200)^(-1.0/200) sage: f_x = cos(u) * cos(v) * (abs(cos(3*v/4))^500+abs(sin(3*v/4))^500)^(-1/260) * K sage: f_y = cos(u) * sin(v) * (abs(cos(3*v/4))^500+abs(sin(3*v/4))^500)^(-1/260) * K sage: f_z = sin(u) * K sage: parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, 0, 2*pi)) Graphics3d Object .. PLOT:: u,v = var('u v') K = (abs(cos(u))**200+abs(sin(u))**200)**(-1.0/200) f_x = cos(u) * cos(v) * (abs(cos(0.75*v))**500+abs(sin(0.75*v))**500)**(-1.0/260) * K f_y = cos(u)*sin(v)*(abs(cos(0.75*v))**500+abs(sin(0.75*v))**500)**(-1.0/260) * K f_z = sin(u) * K P = parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, 0, 2*pi)) sphinx_plot(P) Double heart:: sage: u, v = var('u,v') sage: G1 = abs(sqrt(2)*tanh((u/sqrt(2)))) sage: G2 = abs(sqrt(2)*tanh((v/sqrt(2)))) sage: f_x = (abs(v) - abs(u) - G1 + G2)*sin(v) sage: f_y = (abs(v) - abs(u) - G1 - G2)*cos(v) sage: f_z = sin(u)*(abs(cos(u)) + abs(sin(u)))^(-1) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,pi), (v,-pi,pi)) Graphics3d Object .. PLOT:: u, v = var('u,v') G1 = abs(sqrt(2)*tanh((u/sqrt(2)))) G2 = abs(sqrt(2)*tanh((v/sqrt(2)))) f_x = (abs(v) - abs(u) - G1 + G2)*sin(v) f_y = (abs(v) - abs(u) - G1 - G2)*cos(v) f_z = sin(u)*(abs(cos(u)) + abs(sin(u)))**(-1) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,pi), (v,-pi,pi))) Heart:: sage: u, v = var('u,v') sage: f_x = cos(u)*(4*sqrt(1-v^2)*sin(abs(u))^abs(u)) sage: f_y = sin(u)*(4*sqrt(1-v^2)*sin(abs(u))^abs(u)) sage: f_z = v sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-1,1), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u)*(4*sqrt(1-v**2)*sin(abs(u))**abs(u)) f_y = sin(u) *(4*sqrt(1-v**2)*sin(abs(u))**abs(u)) f_z = v sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-1,1), frame=False, color="red")) A Trefoil knot https://en.wikipedia.org/wiki/Trefoil_knot:: sage: u, v = var('u,v') sage: f_x = (4*(1+0.25*sin(3*v))+cos(u))*cos(2*v) sage: f_y = (4*(1+0.25*sin(3*v))+cos(u))*sin(2*v) sage: f_z = sin(u)+2*cos(3*v) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="blue") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (4*(1+0.25*sin(3*v))+cos(u))*cos(2*v) f_y = (4*(1+0.25*sin(3*v))+cos(u))*sin(2*v) f_z = sin(u)+2*cos(3*v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="blue")) Green bowtie:: sage: u, v = var('u,v') sage: f_x = sin(u) / (sqrt(2) + sin(v)) sage: f_y = sin(u) / (sqrt(2) + cos(v)) sage: f_z = cos(u) / (1 + sqrt(2)) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = sin(u) / (sqrt(2) + sin(v)) f_y = sin(u) / (sqrt(2) + cos(v)) f_z = cos(u) / (1 + sqrt(2)) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="green")) Boy's surface http://en.wikipedia.org/wiki/Boy's_surface and http://mathcurve.com/surfaces/boy/boy.shtml:: sage: u, v = var('u,v') sage: K = cos(u) / (sqrt(2) - cos(2*u)*sin(3*v)) sage: f_x = K * (cos(u)*cos(2*v)+sqrt(2)*sin(u)*cos(v)) sage: f_y = K * (cos(u)*sin(2*v)-sqrt(2)*sin(u)*sin(v)) sage: f_z = 3 * K * cos(u) sage: parametric_plot3d([f_x, f_y, f_z], (u,-2*pi,2*pi), (v,0,pi), ....: plot_points=[90,90], frame=False, color="orange") # long time -- about 30 seconds Graphics3d Object .. PLOT:: u, v = var('u,v') K = cos(u) / (sqrt(2) - cos(2*u)*sin(3*v)) f_x = K * (cos(u)*cos(2*v)+sqrt(2)*sin(u)*cos(v)) f_y = K * (cos(u)*sin(2*v)-sqrt(2)*sin(u)*sin(v)) f_z = 3 * K * cos(u) P = parametric_plot3d([f_x, f_y, f_z], (u,-2*pi,2*pi), (v,0,pi), plot_points=[90,90], frame=False, color="orange") # long time -- about 30 seconds sphinx_plot(P) Maeder's Owl also known as Bour's minimal surface https://en.wikipedia.org/wiki/Bour%27s_minimal_surface:: sage: u, v = var('u,v') sage: f_x = v*cos(u) - 0.5*v^2*cos(2*u) sage: f_y = -v*sin(u) - 0.5*v^2*sin(2*u) sage: f_z = 4 * v^1.5 * cos(3*u/2) / 3 sage: parametric_plot3d([f_x, f_y, f_z], (u,-2*pi,2*pi), (v,0,1), ....: plot_points=[90,90], frame=False, color="purple") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = v*cos(u) - 0.5*v**2*cos(2*u) f_y = -v*sin(u) - 0.5*v**2*sin(2*u) f_z = 4 * v**1.5 * cos(3*u/2) / 3 P = parametric_plot3d([f_x, f_y, f_z], (u,-2*pi,2*pi), (v,0,1), plot_points=[90,90], frame=False, color="purple") sphinx_plot(P) Bracelet:: sage: u, v = var('u,v') sage: f_x = (2 + 0.2*sin(2*pi*u))*sin(pi*v) sage: f_y = 0.2 * cos(2*pi*u) * 3 * cos(2*pi*v) sage: f_z = (2 + 0.2*sin(2*pi*u))*cos(pi*v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,pi/2), (v,0,3*pi/4), frame=False, color="gray") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (2 + 0.2*sin(2*pi*u))*sin(pi*v) f_y = 0.2 * cos(2*pi*u) * 3* cos(2*pi*v) f_z = (2 + 0.2*sin(2*pi*u))*cos(pi*v) P = parametric_plot3d([f_x, f_y, f_z], (u,0,pi/2), (v,0,3*pi/4), frame=False, color="gray") sphinx_plot(P) Green goblet:: sage: u, v = var('u,v') sage: f_x = cos(u) * cos(2*v) sage: f_y = sin(u) * cos(2*v) sage: f_z = sin(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u) * cos(2*v) f_y = sin(u) * cos(2*v) f_z = sin(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,pi), frame=False, color="green")) Funny folded surface - with square projection:: sage: u, v = var('u,v') sage: f_x = cos(u) * sin(2*v) sage: f_y = sin(u) * cos(2*v) sage: f_z = sin(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u) * sin(2*v) f_y = sin(u) * cos(2*v) f_z = sin(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green")) Surface of revolution of figure 8:: sage: u, v = var('u,v') sage: f_x = cos(u) * sin(2*v) sage: f_y = sin(u) * sin(2*v) sage: f_z = sin(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u) * sin(2*v) f_y = sin(u) * sin(2*v) f_z = sin(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green")) Yellow Whitney's umbrella http://en.wikipedia.org/wiki/Whitney_umbrella:: sage: u, v = var('u,v') sage: f_x = u*v sage: f_y = u sage: f_z = v^2 sage: parametric_plot3d([f_x, f_y, f_z], (u,-1,1), (v,-1,1), frame=False, color="yellow") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = u*v f_y = u f_z = v**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-1,1), (v,-1,1), frame=False, color="yellow")) Cross cap http://en.wikipedia.org/wiki/Cross-cap:: sage: u, v = var('u,v') sage: f_x = (1+cos(v)) * cos(u) sage: f_y = (1+cos(v)) * sin(u) sage: f_z = -tanh((2/3)*(u-pi)) * sin(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (1+cos(v)) * cos(u) f_y = (1+cos(v)) * sin(u) f_z = -tanh((2.0/3.0)*(u-pi)) * sin(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red")) Twisted torus:: sage: u, v = var('u,v') sage: f_x = (3+sin(v)+cos(u)) * cos(2*v) sage: f_y = (3+sin(v)+cos(u)) * sin(2*v) sage: f_z = sin(u) + 2*cos(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (3+sin(v)+cos(u)) * cos(2*v) f_y = (3+sin(v)+cos(u)) * sin(2*v) f_z = sin(u) + 2*cos(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red")) Four intersecting discs:: sage: u, v = var('u,v') sage: f_x = v*cos(u) - 0.5*v^2*cos(2*u) sage: f_y = -v*sin(u) - 0.5*v^2*sin(2*u) sage: f_z = 4 * v^1.5 * cos(3*u/2) / 3 sage: parametric_plot3d([f_x, f_y, f_z], (u,0,4*pi), (v,0,2*pi), frame=False, color="red", opacity=0.7) Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = v*cos(u) - 0.5*v**2*cos(2*u) f_y = -v*sin(u) - 0.5*v**2*sin(2*u) f_z = 4 * v**1.5 * cos(3.0*u/2.0) /3 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,4*pi), (v,0,2*pi), frame=False, color="red", opacity=0.7)) Steiner surface/Roman's surface (see http://en.wikipedia.org/wiki/Roman_surface and http://en.wikipedia.org/wiki/Steiner_surface):: sage: u, v = var('u,v') sage: f_x = (sin(2*u) * cos(v) * cos(v)) sage: f_y = (sin(u) * sin(2*v)) sage: f_z = (cos(u) * sin(2*v)) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi/2,pi/2), (v,-pi/2,pi/2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (sin(2*u) * cos(v) * cos(v)) f_y = (sin(u) * sin(2*v)) f_z = (cos(u) * sin(2*v)) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi/2,pi/2), (v,-pi/2,pi/2), frame=False, color="red")) Klein bottle? (see http://en.wikipedia.org/wiki/Klein_bottle):: sage: u, v = var('u,v') sage: f_x = (3*(1+sin(v)) + 2*(1-cos(v)/2)*cos(u)) * cos(v) sage: f_y = (4+2*(1-cos(v)/2)*cos(u)) * sin(v) sage: f_z = -2 * (1-cos(v)/2) * sin(u) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (3*(1+sin(v)) + 2*(1-cos(v)/2)*cos(u)) * cos(v) f_y = (4+2*(1-cos(v)/2)*cos(u)) * sin(v) f_z = -2 * (1-cos(v)/2) * sin(u) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green")) A Figure 8 embedding of the Klein bottle (see http://en.wikipedia.org/wiki/Klein_bottle):: sage: u, v = var('u,v') sage: f_x = (2+cos(v/2)*sin(u)-sin(v/2)*sin(2*u)) * cos(v) sage: f_y = (2+cos(v/2)*sin(u)-sin(v/2)*sin(2*u)) * sin(v) sage: f_z = sin(v/2)*sin(u) + cos(v/2)*sin(2*u) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (2+cos(0.5*v)*sin(u)-sin(0.5*v)*sin(2*u)) * cos(v) f_y = (2+cos(0.5*v)*sin(u)-sin(0.5*v)*sin(2*u)) * sin(v) f_z = sin(v*0.5)*sin(u) + cos(v*0.5)*sin(2*u) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red")) Enneper's surface (see http://en.wikipedia.org/wiki/Enneper_surface):: sage: u, v = var('u,v') sage: f_x = u - u^3/3 + u*v^2 sage: f_y = v - v^3/3 + v*u^2 sage: f_z = u^2 - v^2 sage: parametric_plot3d([f_x, f_y, f_z], (u,-2,2), (v,-2,2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = u - u**3/3 + u*v**2 f_y = v - v**3/3 + v*u**2 f_z = u**2 - v**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-2,2), (v,-2,2), frame=False, color="red")) Henneberg's surface (see http://xahlee.org/surface/gallery_m.html):: sage: u, v = var('u,v') sage: f_x = 2*sinh(u)*cos(v) - (2/3)*sinh(3*u)*cos(3*v) sage: f_y = 2*sinh(u)*sin(v) + (2/3)*sinh(3*u)*sin(3*v) sage: f_z = 2 * cosh(2*u) * cos(2*v) sage: parametric_plot3d([f_x, f_y, f_z], (u,-1,1), (v,-pi/2,pi/2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = 2.0*sinh(u)*cos(v) - (2.0/3.0)*sinh(3*u)*cos(3*v) f_y = 2.0*sinh(u)*sin(v) + (2.0/3.0)*sinh(3*u)*sin(3*v) f_z = 2.0 * cosh(2*u) * cos(2*v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-1,1), (v,-pi/2,pi/2), frame=False, color="red")) Dini's spiral:: sage: u, v = var('u,v') sage: f_x = cos(u) * sin(v) sage: f_y = sin(u) * sin(v) sage: f_z = (cos(v)+log(tan(v/2))) + 0.2*u sage: parametric_plot3d([f_x, f_y, f_z], (u,0,12.4), (v,0.1,2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u) * sin(v) f_y = sin(u) * sin(v) f_z = (cos(v)+log(tan(v*0.5))) + 0.2*u sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,12.4), (v,0.1,2), frame=False, color="red")) Catalan's surface (see http://xahlee.org/surface/catalan/catalan.html):: sage: u, v = var('u,v') sage: f_x = u - sin(u)*cosh(v) sage: f_y = 1 - cos(u)*cosh(v) sage: f_z = 4 * sin(1/2*u) * sinh(v/2) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,3*pi), (v,-2,2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = u - sin(u)*cosh(v) f_y = 1.0 - cos(u)*cosh(v) f_z = 4.0 * sin(0.5*u) * sinh(0.5*v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,3*pi), (v,-2,2), frame=False, color="red")) A Conchoid:: sage: u, v = var('u,v') sage: k = 1.2; k_2 = 1.2; a = 1.5 sage: f = (k^u*(1+cos(v))*cos(u), k^u*(1+cos(v))*sin(u), k^u*sin(v)-a*k_2^u) sage: parametric_plot3d(f, (u,0,6*pi), (v,0,2*pi), plot_points=[40,40], texture=(0,0.5,0)) Graphics3d Object .. PLOT:: u, v = var('u,v') k = 1.2; k_2 = 1.2; a = 1.5 f = (k**u*(1+cos(v))*cos(u), k**u*(1+cos(v))*sin(u), k**u*sin(v)-a*k_2**u) sphinx_plot(parametric_plot3d(f, (u,0,6*pi), (v,0,2*pi), plot_points=[40,40], texture=(0,0.5,0))) A Möbius strip:: sage: u,v = var("u,v") sage: parametric_plot3d([cos(u)*(1+v*cos(u/2)), sin(u)*(1+v*cos(u/2)), 0.2*v*sin(u/2)], ....: (u,0, 4*pi+0.5), (v,0, 0.3), plot_points=[50,50]) Graphics3d Object .. PLOT:: u,v = var("u,v") sphinx_plot(parametric_plot3d([cos(u)*(1+v*cos(u*0.5)), sin(u)*(1+v*cos(u*0.5)), 0.2*v*sin(u*0.5)], (u,0,4*pi+0.5), (v,0,0.3), plot_points=[50,50])) A Twisted Ribbon:: sage: u, v = var('u,v') sage: parametric_plot3d([3*sin(u)*cos(v), 3*sin(u)*sin(v), cos(v)], ....: (u,0,2*pi), (v,0,pi), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([3*sin(u)*cos(v), 3*sin(u)*sin(v), cos(v)], (u,0,2*pi), (v,0,pi), plot_points=[50,50])) An Ellipsoid:: sage: u, v = var('u,v') sage: parametric_plot3d([3*sin(u)*cos(v), 2*sin(u)*sin(v), cos(u)], ....: (u,0, 2*pi), (v, 0, 2*pi), plot_points=[50,50], aspect_ratio=[1,1,1]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([3*sin(u)*cos(v), 2*sin(u)*sin(v), cos(u)], (u,0,2*pi), (v,0,2*pi), plot_points=[50,50], aspect_ratio=[1,1,1])) A Cone:: sage: u, v = var('u,v') sage: parametric_plot3d([u*cos(v), u*sin(v), u], (u,-1,1), (v,0,2*pi+0.5), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([u*cos(v), u*sin(v), u], (u,-1,1), (v,0,2*pi+0.5), plot_points=[50,50])) A Paraboloid:: sage: u, v = var('u,v') sage: parametric_plot3d([u*cos(v), u*sin(v), u^2], (u,0,1), (v,0,2*pi+0.4), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([u*cos(v), u*sin(v), u**2], (u,0,1), (v,0,2*pi+0.4), plot_points=[50,50])) A Hyperboloid:: sage: u, v = var('u,v') sage: plot3d(u^2-v^2, (u,-1,1), (v,-1,1), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(plot3d(u**2-v**2, (u,-1,1), (v,-1,1), plot_points=[50,50])) A weird looking surface - like a Möbius band but also an O:: sage: u, v = var('u,v') sage: parametric_plot3d([sin(u)*cos(u)*log(u^2)*sin(v), (u^2)^(1/6)*(cos(u)^2)^(1/4)*cos(v), sin(v)], ....: (u,0.001,1), (v,-pi,pi+0.2), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([sin(u)*cos(u)*log(u**2)*sin(v), (u**2)**(1.0/6.0)*(cos(u)**2)**(0.25)*cos(v), sin(v)], (u,0.001,1), (v,-pi,pi+0.2), plot_points=[50,50])) A heart, but not a cardioid (for my wife):: sage: u, v = var('u,v') sage: p1 = parametric_plot3d([sin(u)*cos(u)*log(u^2)*v*(1-v)/2, ((u^6)^(1/20)*(cos(u)^2)^(1/4)-1/2)*v*(1-v), v^(0.5)], ....: (u,0.001,1), (v,0,1), plot_points=[70,70], color='red') sage: p2 = parametric_plot3d([-sin(u)*cos(u)*log(u^2)*v*(1-v)/2, ((u^6)^(1/20)*(cos(u)^2)^(1/4)-1/2)*v*(1-v), v^(0.5)], ....: (u, 0.001,1), (v,0,1), plot_points=[70,70], color='red') sage: show(p1+p2) .. PLOT:: u, v = var('u,v') p1 = parametric_plot3d([sin(u)*cos(u)*log(u**2)*v*(1-v)*0.5, ((u**6)**(1/20.0)*(cos(u)**2)**(0.25)-0.5)*v*(1-v), v**(0.5)], (u,0.001,1), (v,0,1), plot_points=[70,70], color='red') p2 = parametric_plot3d([-sin(u)*cos(u)*log(u**2)*v*(1-v)*0.5, ((u**6)**(1/20.0)*(cos(u)**2)**(0.25)-0.5)*v*(1-v), v**(0.5)], (u,0.001,1), (v,0,1), plot_points=[70,70], color='red') sphinx_plot(p1+p2) A Hyperhelicoidal:: sage: u = var("u") sage: v = var("v") sage: f_x = (sinh(v)*cos(3*u)) / (1+cosh(u)*cosh(v)) sage: f_y = (sinh(v)*sin(3*u)) / (1+cosh(u)*cosh(v)) sage: f_z = (cosh(v)*sinh(u)) / (1+cosh(u)*cosh(v)) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), plot_points=[50,50], frame=False, color="red") Graphics3d Object .. PLOT:: u = var("u") v = var("v") f_x = (sinh(v)*cos(3*u)) / (1+cosh(u)*cosh(v)) f_y = (sinh(v)*sin(3*u)) / (1+cosh(u)*cosh(v)) f_z = (cosh(v)*sinh(u)) / (1+cosh(u)*cosh(v)) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), plot_points=[50,50], frame=False, color="red")) A Helicoid (lines through a helix, http://en.wikipedia.org/wiki/Helix):: sage: u, v = var('u,v') sage: f_x = sinh(v) * sin(u) sage: f_y = -sinh(v) * cos(u) sage: f_z = 3 * u sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), plot_points=[50,50], frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = sinh(v) * sin(u) f_y = -sinh(v) * cos(u) f_z = 3 * u sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), plot_points=[50,50], frame=False, color="red")) Kuen's surface (http://virtualmathmuseum.org/Surface/kuen/kuen.html):: sage: f_x = (2*(cos(u) + u*sin(u))*sin(v))/(1+ u^2*sin(v)^2) sage: f_y = (2*(sin(u) - u*cos(u))*sin(v))/(1+ u^2*sin(v)^2) sage: f_z = log(tan(1/2 *v)) + (2*cos(v))/(1+ u^2*sin(v)^2) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0.01,pi-0.01), plot_points=[50,50], frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (2.0*(cos(u)+u*sin(u))*sin(v)) / (1.0+u**2*sin(v)**2) f_y = (2.0*(sin(u)-u*cos(u))*sin(v)) / (1.0+u**2*sin(v)**2) f_z = log(tan(0.5 *v)) + (2*cos(v))/(1.0+u**2*sin(v)**2) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0.01,pi-0.01), plot_points=[50,50], frame=False, color="green")) A 5-pointed star:: sage: G1 = (abs(cos(u/4))^0.5+abs(sin(u/4))^0.5)^(-1/0.3) sage: G2 = (abs(cos(5*v/4))^1.7+abs(sin(5*v/4))^1.7)^(-1/0.1) sage: f_x = cos(u) * cos(v) * G1 * G2 sage: f_y = cos(u) * sin(v) * G1 * G2 sage: f_z = sin(u) * G1 sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi/2,pi/2), (v,0,2*pi), plot_points=[50,50], frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') G1 = (abs(cos(u/4))**0.5+abs(sin(u/4))**0.5)**(-1/0.3) G2 = (abs(cos(5*v/4))**1.7+abs(sin(5*v/4))**1.7)**(-1/0.1) f_x = cos(u) * cos(v) * G1 * G2 f_y = cos(u) * sin(v) * G1 * G2 f_z = sin(u) * G1 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi/2,pi/2), (v,0,2*pi), plot_points=[50,50], frame=False, color="green")) A cool self-intersecting surface (Eppener surface?):: sage: f_x = u - u^3/3 + u*v^2 sage: f_y = v - v^3/3 + v*u^2 sage: f_z = u^2 - v^2 sage: parametric_plot3d([f_x, f_y, f_z], (u,-25,25), (v,-25,25), plot_points=[50,50], frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = u - u**3/3 + u*v**2 f_y = v - v**3/3 + v*u**2 f_z = u**2 - v**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-25,25), (v,-25,25), plot_points=[50,50], frame=False, color="green")) The breather surface (http://en.wikipedia.org/wiki/Breather_surface):: sage: K = sqrt(0.84) sage: G = (0.4*((K*cosh(0.4*u))^2 + (0.4*sin(K*v))^2)) sage: f_x = (2*K*cosh(0.4*u)*(-(K*cos(v)*cos(K*v)) - sin(v)*sin(K*v)))/G sage: f_y = (2*K*cosh(0.4*u)*(-(K*sin(v)*cos(K*v)) + cos(v)*sin(K*v)))/G sage: f_z = -u + (2*0.84*cosh(0.4*u)*sinh(0.4*u))/G sage: parametric_plot3d([f_x, f_y, f_z], (u,-13.2,13.2), (v,-37.4,37.4), plot_points=[90,90], frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') K = sqrt(0.84) G = (0.4*((K*cosh(0.4*u))**2 + (0.4*sin(K*v))**2)) f_x = (2*K*cosh(0.4*u)*(-(K*cos(v)*cos(K*v)) - sin(v)*sin(K*v)))/G f_y = (2*K*cosh(0.4*u)*(-(K*sin(v)*cos(K*v)) + cos(v)*sin(K*v)))/G f_z = -u + (2*0.84*cosh(0.4*u)*sinh(0.4*u))/G sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-13.2,13.2), (v,-37.4,37.4), plot_points=[90,90], frame=False, color="green")) TESTS:: sage: u, v = var('u,v') sage: plot3d(u^2-v^2, (u,-1,1), (u,-1,1)) Traceback (most recent call last): ... ValueError: range variables should be distinct, but there are duplicates From :trac:`2858`:: sage: parametric_plot3d((u,-u,v), (u,-10,10),(v,-10,10)) Graphics3d Object sage: f(u)=u; g(v)=v^2; parametric_plot3d((g,f,f), (-10,10),(-10,10)) Graphics3d Object From :trac:`5368`:: sage: x, y = var('x,y') sage: plot3d(x*y^2 - sin(x), (x,-1,1), (y,-1,1)) Graphics3d Object """ # TODO: # * Surface -- behavior of functions not defined everywhere -- see note above # * Iterative refinement # color_function -- (default: "automatic") how to determine the color of curves and surfaces # color_function_scaling -- (default: True) whether to scale the input to color_function # exclusions -- (default: "automatic") u points or (u,v) conditions to exclude. # (E.g., exclusions could be a function e = lambda u, v: False if u < v else True # exclusions_style -- (default: None) what to draw at excluded points # max_recursion -- (default: "automatic") maximum number of recursive subdivisions, # when ... # mesh -- (default: "automatic") how many mesh divisions in each direction to draw # mesh_functions -- (default: "automatic") how to determine the placement of mesh divisions # mesh_shading -- (default: None) how to shade regions between mesh divisions # plot_range -- (default: "automatic") range of values to include if is_Vector(f): f = tuple(f) if isinstance(f, (list, tuple)) and len(f) > 0 and isinstance(f[0], (list, tuple)): return sum([parametric_plot3d(v, urange, vrange, plot_points=plot_points, **kwds) for v in f]) if not isinstance(f, (tuple, list)) or len(f) != 3: raise ValueError("f must be a list, tuple, or vector of length 3") if vrange is None: if plot_points == "automatic": plot_points = 75 G = _parametric_plot3d_curve(f, urange, plot_points=plot_points, **kwds) else: if plot_points == "automatic": plot_points = [40, 40] G = _parametric_plot3d_surface(f, urange, vrange, plot_points=plot_points, boundary_style=boundary_style, **kwds) G._set_extra_kwds(kwds) return G
def setup_for_eval_on_grid(funcs, ranges, plot_points=None, return_vars=False): """ Calculate the necessary parameters to construct a list of points, and make the functions fast_callable. INPUT: - ``funcs`` - a function, or a list, tuple, or vector of functions - ``ranges`` - a list of ranges. A range can be a 2-tuple of numbers specifying the minimum and maximum, or a 3-tuple giving the variable explicitly. - ``plot_points`` - a tuple of integers specifying the number of plot points for each range. If a single number is specified, it will be the value for all ranges. This defaults to 2. - ``return_vars`` - (default False) If True, return the variables, in order. OUTPUT: - ``fast_funcs`` - if only one function passed, then a fast callable function. If funcs is a list or tuple, then a tuple of fast callable functions is returned. - ``range_specs`` - a list of range_specs: for each range, a tuple is returned of the form (range_min, range_max, range_step) such that ``srange(range_min, range_max, range_step, include_endpoint=True)`` gives the correct points for evaluation. EXAMPLES:: sage: x,y,z=var('x,y,z') sage: f(x,y)=x+y-z sage: g(x,y)=x+y sage: h(y)=-y sage: sage.plot.misc.setup_for_eval_on_grid(f, [(0, 2),(1,3),(-4,1)], plot_points=5) (<sage.ext...>, [(0.0, 2.0, 0.5), (1.0, 3.0, 0.5), (-4.0, 1.0, 1.25)]) sage: sage.plot.misc.setup_for_eval_on_grid([g,h], [(0, 2),(-1,1)], plot_points=5) ((<sage.ext...>, <sage.ext...>), [(0.0, 2.0, 0.5), (-1.0, 1.0, 0.5)]) sage: sage.plot.misc.setup_for_eval_on_grid([sin,cos], [(-1,1)], plot_points=9) ((<sage.ext...>, <sage.ext...>), [(-1.0, 1.0, 0.25)]) sage: sage.plot.misc.setup_for_eval_on_grid([lambda x: x^2,cos], [(-1,1)], plot_points=9) ((<function <lambda> ...>, <sage.ext...>), [(-1.0, 1.0, 0.25)]) sage: sage.plot.misc.setup_for_eval_on_grid([x+y], [(x,-1,1),(y,-2,2)]) ((<sage.ext...>,), [(-1.0, 1.0, 2.0), (-2.0, 2.0, 4.0)]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,-1,1),(y,-1,1)], plot_points=[4,9]) (<sage.ext...>, [(-1.0, 1.0, 0.6666666666666666), (-1.0, 1.0, 0.25)]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,-1,1),(y,-1,1)], plot_points=[4,9,10]) Traceback (most recent call last): ... ValueError: plot_points must be either an integer or a list of integers, one for each range sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(1,-1),(y,-1,1)], plot_points=[4,9,10]) Traceback (most recent call last): ... ValueError: Some variable ranges specify variables while others do not sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(1,-1),(-1,1)], plot_points=5) doctest:...: DeprecationWarning: Unnamed ranges for more than one variable is deprecated and will be removed from a future release of Sage; you can used named ranges instead, like (x,0,2) See http://trac.sagemath.org/7008 for details. (<sage.ext...>, [(1.0, -1.0, 0.5), (-1.0, 1.0, 0.5)]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(y,1,-1),(x,-1,1)], plot_points=5) (<sage.ext...>, [(1.0, -1.0, 0.5), (-1.0, 1.0, 0.5)]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,1,-1),(x,-1,1)], plot_points=5) Traceback (most recent call last): ... ValueError: range variables should be distinct, but there are duplicates sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,1,1),(y,-1,1)]) Traceback (most recent call last): ... ValueError: plot start point and end point must be different sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,1,-1),(y,-1,1)], return_vars=True) (<sage.ext...>, [(1.0, -1.0, 2.0), (-1.0, 1.0, 2.0)], [x, y]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(y,1,-1),(x,-1,1)], return_vars=True) (<sage.ext...>, [(1.0, -1.0, 2.0), (-1.0, 1.0, 2.0)], [y, x]) """ if max(map(len, ranges)) != min(map(len, ranges)): raise ValueError("Some variable ranges specify variables while others do not") if len(ranges[0])==3: vars = [r[0] for r in ranges] ranges = [r[1:] for r in ranges] if len(set(vars))<len(vars): raise ValueError("range variables should be distinct, but there are duplicates") else: vars, free_vars = unify_arguments(funcs) if len(free_vars)>1: from sage.misc.superseded import deprecation deprecation(7008, "Unnamed ranges for more than one variable is deprecated and will be removed from a future release of Sage; you can used named ranges instead, like (x,0,2)") # pad the variables if we don't have enough nargs = len(ranges) if len(vars)<nargs: vars += ('_',)*(nargs-len(vars)) ranges = [[float(z) for z in r] for r in ranges] if plot_points is None: plot_points=2 if not isinstance(plot_points, (list, tuple)): plot_points = [plot_points]*len(ranges) elif len(plot_points)!=nargs: raise ValueError("plot_points must be either an integer or a list of integers, one for each range") plot_points = [int(p) if p>=2 else 2 for p in plot_points] range_steps = [abs(range[1] - range[0])/(p-1) for range, p in zip(ranges, plot_points)] if min(range_steps) == float(0): raise ValueError("plot start point and end point must be different") options={} if nargs==1: options['expect_one_var']=True if is_Vector(funcs): funcs = list(funcs) #TODO: raise an error if there is a function/method in funcs that takes more values than we have ranges if return_vars: return fast_float(funcs, *vars,**options), [tuple(range+[range_step]) for range,range_step in zip(ranges, range_steps)], vars else: return fast_float(funcs, *vars,**options), [tuple(range+[range_step]) for range,range_step in zip(ranges, range_steps)]
def setup_for_eval_on_grid(funcs, ranges, plot_points=None, return_vars=False): """ Calculate the necessary parameters to construct a list of points, and make the functions fast_callable. INPUT: - ``funcs`` -- a function, or a list, tuple, or vector of functions - ``ranges`` -- a list of ranges. A range can be a 2-tuple of numbers specifying the minimum and maximum, or a 3-tuple giving the variable explicitly. - ``plot_points`` -- a tuple of integers specifying the number of plot points for each range. If a single number is specified, it will be the value for all ranges. This defaults to 2. - ``return_vars`` -- (default ``False``) If ``True``, return the variables, in order. OUTPUT: - ``fast_funcs`` - if only one function passed, then a fast callable function. If funcs is a list or tuple, then a tuple of fast callable functions is returned. - ``range_specs`` - a list of range_specs: for each range, a tuple is returned of the form (range_min, range_max, range_step) such that ``srange(range_min, range_max, range_step, include_endpoint=True)`` gives the correct points for evaluation. EXAMPLES:: sage: x,y,z=var('x,y,z') sage: f(x,y)=x+y-z sage: g(x,y)=x+y sage: h(y)=-y sage: sage.plot.misc.setup_for_eval_on_grid(f, [(0, 2),(1,3),(-4,1)], plot_points=5) (<sage.ext...>, [(0.0, 2.0, 0.5), (1.0, 3.0, 0.5), (-4.0, 1.0, 1.25)]) sage: sage.plot.misc.setup_for_eval_on_grid([g,h], [(0, 2),(-1,1)], plot_points=5) ((<sage.ext...>, <sage.ext...>), [(0.0, 2.0, 0.5), (-1.0, 1.0, 0.5)]) sage: sage.plot.misc.setup_for_eval_on_grid([sin,cos], [(-1,1)], plot_points=9) ((<sage.ext...>, <sage.ext...>), [(-1.0, 1.0, 0.25)]) sage: sage.plot.misc.setup_for_eval_on_grid([lambda x: x^2,cos], [(-1,1)], plot_points=9) ((<function <lambda> ...>, <sage.ext...>), [(-1.0, 1.0, 0.25)]) sage: sage.plot.misc.setup_for_eval_on_grid([x+y], [(x,-1,1),(y,-2,2)]) ((<sage.ext...>,), [(-1.0, 1.0, 2.0), (-2.0, 2.0, 4.0)]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,-1,1),(y,-1,1)], plot_points=[4,9]) (<sage.ext...>, [(-1.0, 1.0, 0.6666666666666666), (-1.0, 1.0, 0.25)]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,-1,1),(y,-1,1)], plot_points=[4,9,10]) Traceback (most recent call last): ... ValueError: plot_points must be either an integer or a list of integers, one for each range sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(1,-1),(y,-1,1)], plot_points=[4,9,10]) Traceback (most recent call last): ... ValueError: Some variable ranges specify variables while others do not sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(1,-1),(-1,1)], plot_points=5) doctest:...: DeprecationWarning: Unnamed ranges for more than one variable is deprecated and will be removed from a future release of Sage; you can used named ranges instead, like (x,0,2) See http://trac.sagemath.org/7008 for details. (<sage.ext...>, [(1.0, -1.0, 0.5), (-1.0, 1.0, 0.5)]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(y,1,-1),(x,-1,1)], plot_points=5) (<sage.ext...>, [(1.0, -1.0, 0.5), (-1.0, 1.0, 0.5)]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,1,-1),(x,-1,1)], plot_points=5) Traceback (most recent call last): ... ValueError: range variables should be distinct, but there are duplicates sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,1,1),(y,-1,1)]) Traceback (most recent call last): ... ValueError: plot start point and end point must be different sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(x,1,-1),(y,-1,1)], return_vars=True) (<sage.ext...>, [(1.0, -1.0, 2.0), (-1.0, 1.0, 2.0)], [x, y]) sage: sage.plot.misc.setup_for_eval_on_grid(x+y, [(y,1,-1),(x,-1,1)], return_vars=True) (<sage.ext...>, [(1.0, -1.0, 2.0), (-1.0, 1.0, 2.0)], [y, x]) """ if max(map(len, ranges)) != min(map(len, ranges)): raise ValueError( "Some variable ranges specify variables while others do not") if len(ranges[0]) == 3: vars = [r[0] for r in ranges] ranges = [r[1:] for r in ranges] if len(set(vars)) < len(vars): raise ValueError( "range variables should be distinct, but there are duplicates") else: vars, free_vars = unify_arguments(funcs) if len(free_vars) > 1: from sage.misc.superseded import deprecation deprecation( 7008, "Unnamed ranges for more than one variable is deprecated and will be removed from a future release of Sage; you can used named ranges instead, like (x,0,2)" ) # pad the variables if we don't have enough nargs = len(ranges) if len(vars) < nargs: vars += ('_', ) * (nargs - len(vars)) ranges = [[float(z) for z in r] for r in ranges] if plot_points is None: plot_points = 2 if not isinstance(plot_points, (list, tuple)): plot_points = [plot_points] * len(ranges) elif len(plot_points) != nargs: raise ValueError( "plot_points must be either an integer or a list of integers, one for each range" ) plot_points = [int(p) if p >= 2 else 2 for p in plot_points] range_steps = [ abs(range[1] - range[0]) / (p - 1) for range, p in zip(ranges, plot_points) ] if min(range_steps) == float(0): raise ValueError("plot start point and end point must be different") options = {} if nargs == 1: options['expect_one_var'] = True if is_Vector(funcs): funcs = list(funcs) #TODO: raise an error if there is a function/method in funcs that takes more values than we have ranges if return_vars: return fast_float(funcs, *vars, **options), [ tuple(range + [range_step]) for range, range_step in zip(ranges, range_steps) ], vars else: return fast_float(funcs, *vars, **options), [ tuple(range + [range_step]) for range, range_step in zip(ranges, range_steps) ]
def parametric_plot3d(f, urange, vrange=None, plot_points="automatic", boundary_style=None, **kwds): r""" Return a parametric three-dimensional space curve or surface. There are four ways to call this function: - ``parametric_plot3d([f_x, f_y, f_z], (u_min, u_max))``: `f_x, f_y, f_z` are three functions and `u_{\min}` and `u_{\max}` are real numbers - ``parametric_plot3d([f_x, f_y, f_z], (u, u_min, u_max))``: `f_x, f_y, f_z` can be viewed as functions of `u` - ``parametric_plot3d([f_x, f_y, f_z], (u_min, u_max), (v_min, v_max))``: `f_x, f_y, f_z` are each functions of two variables - ``parametric_plot3d([f_x, f_y, f_z], (u, u_min, u_max), (v, v_min, v_max))``: `f_x, f_y, f_z` can be viewed as functions of `u` and `v` INPUT: - ``f`` - a 3-tuple of functions or expressions, or vector of size 3 - ``urange`` - a 2-tuple (u_min, u_max) or a 3-tuple (u, u_min, u_max) - ``vrange`` - (optional - only used for surfaces) a 2-tuple (v_min, v_max) or a 3-tuple (v, v_min, v_max) - ``plot_points`` - (default: "automatic", which is 75 for curves and [40,40] for surfaces) initial number of sample points in each parameter; an integer for a curve, and a pair of integers for a surface. - ``boundary_style`` - (default: None, no boundary) a dict that describes how to draw the boundaries of regions by giving options that are passed to the line3d command. - ``mesh`` - bool (default: False) whether to display mesh grid lines - ``dots`` - bool (default: False) whether to display dots at mesh grid points .. note:: #. By default for a curve any points where `f_x`, `f_y`, or `f_z` do not evaluate to a real number are skipped. #. Currently for a surface `f_x`, `f_y`, and `f_z` have to be defined everywhere. This will change. #. mesh and dots are not supported when using the Tachyon ray tracer renderer. EXAMPLES: We demonstrate each of the four ways to call this function. #. A space curve defined by three functions of 1 variable: :: sage: parametric_plot3d((sin, cos, lambda u: u/10), (0,20)) Graphics3d Object .. PLOT:: sphinx_plot(parametric_plot3d((sin, cos, lambda u: u/10), (0,20))) Note above the lambda function, which creates a callable Python function that sends `u` to `u/10`. #. Next we draw the same plot as above, but using symbolic functions: :: sage: u = var('u') sage: parametric_plot3d((sin(u), cos(u), u/10), (u,0,20)) Graphics3d Object .. PLOT:: u = var('u') sphinx_plot(parametric_plot3d((sin(u), cos(u), u/10), (u,0,20))) #. We draw a parametric surface using 3 Python functions (defined using lambda): :: sage: f = (lambda u,v: cos(u), lambda u,v: sin(u)+cos(v), lambda u,v: sin(v)) sage: parametric_plot3d(f, (0,2*pi), (-pi,pi)) Graphics3d Object .. PLOT:: f = (lambda u,v: cos(u), lambda u,v: sin(u)+cos(v), lambda u,v: sin(v)) sphinx_plot(parametric_plot3d(f, (0,2*pi), (-pi,pi))) #. The same surface, but where the defining functions are symbolic: :: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi)) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d((cos(u), sin(u)+cos(v) ,sin(v)), (u,0,2*pi), (v,-pi,pi))) The surface, but with a mesh:: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), mesh=True) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), mesh=True)) We increase the number of plot points, and make the surface green and transparent:: sage: parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), ....: color='green', opacity=0.1, plot_points=[30,30]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), color='green', opacity=0.1, plot_points=[30,30])) One can also color the surface using a coloring function and a colormap:: sage: u,v = var('u,v') sage: def cf(u,v): return sin(u+v/2)**2 sage: P = parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), ....: (u,0,2*pi), (v,-pi,pi), color=(cf,colormaps.PiYG), plot_points=[60,60]) sage: P.show(viewer='tachyon') .. PLOT:: u,v = var('u,v') def cf(u,v): return sin(u+v/2)**2 P = parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,2*pi), (v,-pi,pi), color=(cf,colormaps.PiYG), plot_points=[60,60]) sphinx_plot(P) Another example, a colored Möbius band:: sage: cm = colormaps.ocean sage: def c(x,y): return sin(x*y)**2 sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip sage: MoebiusStrip(5, 1, plot_points=200, color=(c,cm)) Graphics3d Object .. PLOT:: cm = colormaps.ocean def c(x,y): return sin(x*y)**2 from sage.plot.plot3d.parametric_surface import MoebiusStrip sphinx_plot(MoebiusStrip(5, 1, plot_points=200, color=(c,cm))) Yet another colored example:: sage: from sage.plot.plot3d.parametric_surface import ParametricSurface sage: cm = colormaps.autumn sage: def c(x,y): return sin(x*y)**2 sage: def g(x,y): return x, y+sin(y), x**2 + y**2 sage: ParametricSurface(g, (srange(-10,10,0.1), srange(-5,5.0,0.1)), color=(c,cm)) Graphics3d Object .. PLOT:: from sage.plot.plot3d.parametric_surface import ParametricSurface cm = colormaps.autumn def c(x,y): return sin(x*y)**2 def g(x,y): return x, y+sin(y), x**2 + y**2 sphinx_plot(ParametricSurface(g, (srange(-10,10,0.1), srange(-5,5.0,0.1)), color=(c,cm))) .. 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). We call the space curve function but with polynomials instead of symbolic variables. :: sage: R.<t> = RDF[] sage: parametric_plot3d((t, t^2, t^3), (t,0,3)) Graphics3d Object .. PLOT:: t = var('t') R = RDF['t'] sphinx_plot(parametric_plot3d((t, t**2, t**3), (t,0,3))) Next we plot the same curve, but because we use (0, 3) instead of (t, 0, 3), each polynomial is viewed as a callable function of one variable:: sage: parametric_plot3d((t, t^2, t^3), (0,3)) Graphics3d Object .. PLOT:: t = var('t') R = RDF['t'] sphinx_plot(parametric_plot3d((t, t**2, t**3), (0,3))) We do a plot but mix a symbolic input, and an integer:: sage: t = var('t') sage: parametric_plot3d((1, sin(t), cos(t)), (t,0,3)) Graphics3d Object .. PLOT:: t = var('t') sphinx_plot(parametric_plot3d((1, sin(t), cos(t)), (t,0,3))) We specify a boundary style to show us the values of the function at its extrema:: sage: u, v = var('u,v') sage: parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,pi), (v,0,pi), ....: boundary_style={"color": "black", "thickness": 2}) Graphics3d Object .. PLOT:: u, v = var('u,v') P = parametric_plot3d((cos(u), sin(u)+cos(v), sin(v)), (u,0,pi), (v,0,pi), boundary_style={"color":"black", "thickness":2}) sphinx_plot(P) We can plot vectors:: sage: x,y = var('x,y') sage: parametric_plot3d(vector([x-y, x*y, x*cos(y)]), (x,0,2), (y,0,2)) Graphics3d Object .. PLOT:: x,y = var('x,y') sphinx_plot(parametric_plot3d(vector([x-y, x*y, x*cos(y)]), (x,0,2), (y,0,2))) :: sage: t = var('t') sage: p = vector([1,2,3]) sage: q = vector([2,-1,2]) sage: parametric_plot3d(p*t+q, (t,0,2)) Graphics3d Object .. PLOT:: t = var('t') p = vector([1,2,3]) q = vector([2,-1,2]) sphinx_plot(parametric_plot3d(p*t+q, (t,0,2))) Any options you would normally use to specify the appearance of a curve are valid as entries in the ``boundary_style`` dict. MANY MORE EXAMPLES: We plot two interlinked tori:: sage: u, v = var('u,v') sage: f1 = (4+(3+cos(v))*sin(u), 4+(3+cos(v))*cos(u), 4+sin(v)) sage: f2 = (8+(3+cos(v))*cos(u), 3+sin(v), 4+(3+cos(v))*sin(u)) sage: p1 = parametric_plot3d(f1, (u,0,2*pi), (v,0,2*pi), texture="red") sage: p2 = parametric_plot3d(f2, (u,0,2*pi), (v,0,2*pi), texture="blue") sage: p1 + p2 Graphics3d Object .. PLOT:: u, v = var('u,v') f1 = (4+(3+cos(v))*sin(u), 4+(3+cos(v))*cos(u), 4+sin(v)) f2 = (8+(3+cos(v))*cos(u), 3+sin(v), 4+(3+cos(v))*sin(u)) p1 = parametric_plot3d(f1, (u,0,2*pi), (v,0,2*pi), texture="red") p2 = parametric_plot3d(f2, (u,0,2*pi), (v,0,2*pi), texture="blue") sphinx_plot(p1 + p2) A cylindrical Star of David:: sage: u,v = var('u v') sage: K = (abs(cos(u))^200+abs(sin(u))^200)^(-1.0/200) sage: f_x = cos(u) * cos(v) * (abs(cos(3*v/4))^500+abs(sin(3*v/4))^500)^(-1/260) * K sage: f_y = cos(u) * sin(v) * (abs(cos(3*v/4))^500+abs(sin(3*v/4))^500)^(-1/260) * K sage: f_z = sin(u) * K sage: parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, 0, 2*pi)) Graphics3d Object .. PLOT:: u,v = var('u v') K = (abs(cos(u))**200+abs(sin(u))**200)**(-1.0/200) f_x = cos(u) * cos(v) * (abs(cos(0.75*v))**500+abs(sin(0.75*v))**500)**(-1.0/260) * K f_y = cos(u)*sin(v)*(abs(cos(0.75*v))**500+abs(sin(0.75*v))**500)**(-1.0/260) * K f_z = sin(u) * K P = parametric_plot3d([f_x, f_y, f_z], (u, -pi, pi), (v, 0, 2*pi)) sphinx_plot(P) Double heart:: sage: u, v = var('u,v') sage: G1 = abs(sqrt(2)*tanh((u/sqrt(2)))) sage: G2 = abs(sqrt(2)*tanh((v/sqrt(2)))) sage: f_x = (abs(v) - abs(u) - G1 + G2)*sin(v) sage: f_y = (abs(v) - abs(u) - G1 - G2)*cos(v) sage: f_z = sin(u)*(abs(cos(u)) + abs(sin(u)))^(-1) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,pi), (v,-pi,pi)) Graphics3d Object .. PLOT:: u, v = var('u,v') G1 = abs(sqrt(2)*tanh((u/sqrt(2)))) G2 = abs(sqrt(2)*tanh((v/sqrt(2)))) f_x = (abs(v) - abs(u) - G1 + G2)*sin(v) f_y = (abs(v) - abs(u) - G1 - G2)*cos(v) f_z = sin(u)*(abs(cos(u)) + abs(sin(u)))**(-1) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,pi), (v,-pi,pi))) Heart:: sage: u, v = var('u,v') sage: f_x = cos(u)*(4*sqrt(1-v^2)*sin(abs(u))^abs(u)) sage: f_y = sin(u)*(4*sqrt(1-v^2)*sin(abs(u))^abs(u)) sage: f_z = v sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-1,1), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u)*(4*sqrt(1-v**2)*sin(abs(u))**abs(u)) f_y = sin(u) *(4*sqrt(1-v**2)*sin(abs(u))**abs(u)) f_z = v sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-1,1), frame=False, color="red")) A Trefoil knot (:wikipedia:`Trefoil_knot`):: sage: u, v = var('u,v') sage: f_x = (4*(1+0.25*sin(3*v))+cos(u))*cos(2*v) sage: f_y = (4*(1+0.25*sin(3*v))+cos(u))*sin(2*v) sage: f_z = sin(u)+2*cos(3*v) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="blue") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (4*(1+0.25*sin(3*v))+cos(u))*cos(2*v) f_y = (4*(1+0.25*sin(3*v))+cos(u))*sin(2*v) f_z = sin(u)+2*cos(3*v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="blue")) Green bowtie:: sage: u, v = var('u,v') sage: f_x = sin(u) / (sqrt(2) + sin(v)) sage: f_y = sin(u) / (sqrt(2) + cos(v)) sage: f_z = cos(u) / (1 + sqrt(2)) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = sin(u) / (sqrt(2) + sin(v)) f_y = sin(u) / (sqrt(2) + cos(v)) f_z = cos(u) / (1 + sqrt(2)) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), frame=False, color="green")) Boy's surface (:wikipedia:`Boy's_surface` and https://mathcurve.com/surfaces/boy/boy.shtml):: sage: u, v = var('u,v') sage: K = cos(u) / (sqrt(2) - cos(2*u)*sin(3*v)) sage: f_x = K * (cos(u)*cos(2*v)+sqrt(2)*sin(u)*cos(v)) sage: f_y = K * (cos(u)*sin(2*v)-sqrt(2)*sin(u)*sin(v)) sage: f_z = 3 * K * cos(u) sage: parametric_plot3d([f_x, f_y, f_z], (u,-2*pi,2*pi), (v,0,pi), ....: plot_points=[90,90], frame=False, color="orange") # long time -- about 30 seconds Graphics3d Object .. PLOT:: u, v = var('u,v') K = cos(u) / (sqrt(2) - cos(2*u)*sin(3*v)) f_x = K * (cos(u)*cos(2*v)+sqrt(2)*sin(u)*cos(v)) f_y = K * (cos(u)*sin(2*v)-sqrt(2)*sin(u)*sin(v)) f_z = 3 * K * cos(u) P = parametric_plot3d([f_x, f_y, f_z], (u,-2*pi,2*pi), (v,0,pi), plot_points=[90,90], frame=False, color="orange") # long time -- about 30 seconds sphinx_plot(P) Maeder's Owl also known as Bour's minimal surface (:wikipedia:`Bour%27s_minimal_surface`):: sage: u, v = var('u,v') sage: f_x = v*cos(u) - 0.5*v^2*cos(2*u) sage: f_y = -v*sin(u) - 0.5*v^2*sin(2*u) sage: f_z = 4 * v^1.5 * cos(3*u/2) / 3 sage: parametric_plot3d([f_x, f_y, f_z], (u,-2*pi,2*pi), (v,0,1), ....: plot_points=[90,90], frame=False, color="purple") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = v*cos(u) - 0.5*v**2*cos(2*u) f_y = -v*sin(u) - 0.5*v**2*sin(2*u) f_z = 4 * v**1.5 * cos(3*u/2) / 3 P = parametric_plot3d([f_x, f_y, f_z], (u,-2*pi,2*pi), (v,0,1), plot_points=[90,90], frame=False, color="purple") sphinx_plot(P) Bracelet:: sage: u, v = var('u,v') sage: f_x = (2 + 0.2*sin(2*pi*u))*sin(pi*v) sage: f_y = 0.2 * cos(2*pi*u) * 3 * cos(2*pi*v) sage: f_z = (2 + 0.2*sin(2*pi*u))*cos(pi*v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,pi/2), (v,0,3*pi/4), frame=False, color="gray") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (2 + 0.2*sin(2*pi*u))*sin(pi*v) f_y = 0.2 * cos(2*pi*u) * 3* cos(2*pi*v) f_z = (2 + 0.2*sin(2*pi*u))*cos(pi*v) P = parametric_plot3d([f_x, f_y, f_z], (u,0,pi/2), (v,0,3*pi/4), frame=False, color="gray") sphinx_plot(P) Green goblet:: sage: u, v = var('u,v') sage: f_x = cos(u) * cos(2*v) sage: f_y = sin(u) * cos(2*v) sage: f_z = sin(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u) * cos(2*v) f_y = sin(u) * cos(2*v) f_z = sin(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,pi), frame=False, color="green")) Funny folded surface - with square projection:: sage: u, v = var('u,v') sage: f_x = cos(u) * sin(2*v) sage: f_y = sin(u) * cos(2*v) sage: f_z = sin(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u) * sin(2*v) f_y = sin(u) * cos(2*v) f_z = sin(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green")) Surface of revolution of figure 8:: sage: u, v = var('u,v') sage: f_x = cos(u) * sin(2*v) sage: f_y = sin(u) * sin(2*v) sage: f_z = sin(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u) * sin(2*v) f_y = sin(u) * sin(2*v) f_z = sin(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green")) Yellow Whitney's umbrella (:wikipedia:`Whitney_umbrella`):: sage: u, v = var('u,v') sage: f_x = u*v sage: f_y = u sage: f_z = v^2 sage: parametric_plot3d([f_x, f_y, f_z], (u,-1,1), (v,-1,1), frame=False, color="yellow") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = u*v f_y = u f_z = v**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-1,1), (v,-1,1), frame=False, color="yellow")) Cross cap (:wikipedia:`Cross-cap`):: sage: u, v = var('u,v') sage: f_x = (1+cos(v)) * cos(u) sage: f_y = (1+cos(v)) * sin(u) sage: f_z = -tanh((2/3)*(u-pi)) * sin(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (1+cos(v)) * cos(u) f_y = (1+cos(v)) * sin(u) f_z = -tanh((2.0/3.0)*(u-pi)) * sin(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red")) Twisted torus:: sage: u, v = var('u,v') sage: f_x = (3+sin(v)+cos(u)) * cos(2*v) sage: f_y = (3+sin(v)+cos(u)) * sin(2*v) sage: f_z = sin(u) + 2*cos(v) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (3+sin(v)+cos(u)) * cos(2*v) f_y = (3+sin(v)+cos(u)) * sin(2*v) f_z = sin(u) + 2*cos(v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red")) Four intersecting discs:: sage: u, v = var('u,v') sage: f_x = v*cos(u) - 0.5*v^2*cos(2*u) sage: f_y = -v*sin(u) - 0.5*v^2*sin(2*u) sage: f_z = 4 * v^1.5 * cos(3*u/2) / 3 sage: parametric_plot3d([f_x, f_y, f_z], (u,0,4*pi), (v,0,2*pi), frame=False, color="red", opacity=0.7) Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = v*cos(u) - 0.5*v**2*cos(2*u) f_y = -v*sin(u) - 0.5*v**2*sin(2*u) f_z = 4 * v**1.5 * cos(3.0*u/2.0) /3 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,4*pi), (v,0,2*pi), frame=False, color="red", opacity=0.7)) Steiner surface/Roman's surface (see :wikipedia:`Roman_surface` and :wikipedia:`Steiner_surface`):: sage: u, v = var('u,v') sage: f_x = (sin(2*u) * cos(v) * cos(v)) sage: f_y = (sin(u) * sin(2*v)) sage: f_z = (cos(u) * sin(2*v)) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi/2,pi/2), (v,-pi/2,pi/2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (sin(2*u) * cos(v) * cos(v)) f_y = (sin(u) * sin(2*v)) f_z = (cos(u) * sin(2*v)) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi/2,pi/2), (v,-pi/2,pi/2), frame=False, color="red")) Klein bottle? (see :wikipedia:`Klein_bottle`):: sage: u, v = var('u,v') sage: f_x = (3*(1+sin(v)) + 2*(1-cos(v)/2)*cos(u)) * cos(v) sage: f_y = (4+2*(1-cos(v)/2)*cos(u)) * sin(v) sage: f_z = -2 * (1-cos(v)/2) * sin(u) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (3*(1+sin(v)) + 2*(1-cos(v)/2)*cos(u)) * cos(v) f_y = (4+2*(1-cos(v)/2)*cos(u)) * sin(v) f_z = -2 * (1-cos(v)/2) * sin(u) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="green")) A Figure 8 embedding of the Klein bottle (see :wikipedia:`Klein_bottle`):: sage: u, v = var('u,v') sage: f_x = (2+cos(v/2)*sin(u)-sin(v/2)*sin(2*u)) * cos(v) sage: f_y = (2+cos(v/2)*sin(u)-sin(v/2)*sin(2*u)) * sin(v) sage: f_z = sin(v/2)*sin(u) + cos(v/2)*sin(2*u) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (2+cos(0.5*v)*sin(u)-sin(0.5*v)*sin(2*u)) * cos(v) f_y = (2+cos(0.5*v)*sin(u)-sin(0.5*v)*sin(2*u)) * sin(v) f_z = sin(v*0.5)*sin(u) + cos(v*0.5)*sin(2*u) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0,2*pi), frame=False, color="red")) Enneper's surface (see :wikipedia:`Enneper_surface`):: sage: u, v = var('u,v') sage: f_x = u - u^3/3 + u*v^2 sage: f_y = v - v^3/3 + v*u^2 sage: f_z = u^2 - v^2 sage: parametric_plot3d([f_x, f_y, f_z], (u,-2,2), (v,-2,2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = u - u**3/3 + u*v**2 f_y = v - v**3/3 + v*u**2 f_z = u**2 - v**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-2,2), (v,-2,2), frame=False, color="red")) Henneberg's surface (see http://xahlee.org/surface/gallery_m.html):: sage: u, v = var('u,v') sage: f_x = 2*sinh(u)*cos(v) - (2/3)*sinh(3*u)*cos(3*v) sage: f_y = 2*sinh(u)*sin(v) + (2/3)*sinh(3*u)*sin(3*v) sage: f_z = 2 * cosh(2*u) * cos(2*v) sage: parametric_plot3d([f_x, f_y, f_z], (u,-1,1), (v,-pi/2,pi/2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = 2.0*sinh(u)*cos(v) - (2.0/3.0)*sinh(3*u)*cos(3*v) f_y = 2.0*sinh(u)*sin(v) + (2.0/3.0)*sinh(3*u)*sin(3*v) f_z = 2.0 * cosh(2*u) * cos(2*v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-1,1), (v,-pi/2,pi/2), frame=False, color="red")) Dini's spiral:: sage: u, v = var('u,v') sage: f_x = cos(u) * sin(v) sage: f_y = sin(u) * sin(v) sage: f_z = (cos(v)+log(tan(v/2))) + 0.2*u sage: parametric_plot3d([f_x, f_y, f_z], (u,0,12.4), (v,0.1,2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = cos(u) * sin(v) f_y = sin(u) * sin(v) f_z = (cos(v)+log(tan(v*0.5))) + 0.2*u sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,12.4), (v,0.1,2), frame=False, color="red")) Catalan's surface (see http://xahlee.org/surface/catalan/catalan.html):: sage: u, v = var('u,v') sage: f_x = u - sin(u)*cosh(v) sage: f_y = 1 - cos(u)*cosh(v) sage: f_z = 4 * sin(1/2*u) * sinh(v/2) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,3*pi), (v,-2,2), frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = u - sin(u)*cosh(v) f_y = 1.0 - cos(u)*cosh(v) f_z = 4.0 * sin(0.5*u) * sinh(0.5*v) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,3*pi), (v,-2,2), frame=False, color="red")) A Conchoid:: sage: u, v = var('u,v') sage: k = 1.2; k_2 = 1.2; a = 1.5 sage: f = (k^u*(1+cos(v))*cos(u), k^u*(1+cos(v))*sin(u), k^u*sin(v)-a*k_2^u) sage: parametric_plot3d(f, (u,0,6*pi), (v,0,2*pi), plot_points=[40,40], texture=(0,0.5,0)) Graphics3d Object .. PLOT:: u, v = var('u,v') k = 1.2; k_2 = 1.2; a = 1.5 f = (k**u*(1+cos(v))*cos(u), k**u*(1+cos(v))*sin(u), k**u*sin(v)-a*k_2**u) sphinx_plot(parametric_plot3d(f, (u,0,6*pi), (v,0,2*pi), plot_points=[40,40], texture=(0,0.5,0))) A Möbius strip:: sage: u,v = var("u,v") sage: parametric_plot3d([cos(u)*(1+v*cos(u/2)), sin(u)*(1+v*cos(u/2)), 0.2*v*sin(u/2)], ....: (u,0, 4*pi+0.5), (v,0, 0.3), plot_points=[50,50]) Graphics3d Object .. PLOT:: u,v = var("u,v") sphinx_plot(parametric_plot3d([cos(u)*(1+v*cos(u*0.5)), sin(u)*(1+v*cos(u*0.5)), 0.2*v*sin(u*0.5)], (u,0,4*pi+0.5), (v,0,0.3), plot_points=[50,50])) A Twisted Ribbon:: sage: u, v = var('u,v') sage: parametric_plot3d([3*sin(u)*cos(v), 3*sin(u)*sin(v), cos(v)], ....: (u,0,2*pi), (v,0,pi), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([3*sin(u)*cos(v), 3*sin(u)*sin(v), cos(v)], (u,0,2*pi), (v,0,pi), plot_points=[50,50])) An Ellipsoid:: sage: u, v = var('u,v') sage: parametric_plot3d([3*sin(u)*cos(v), 2*sin(u)*sin(v), cos(u)], ....: (u,0, 2*pi), (v, 0, 2*pi), plot_points=[50,50], aspect_ratio=[1,1,1]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([3*sin(u)*cos(v), 2*sin(u)*sin(v), cos(u)], (u,0,2*pi), (v,0,2*pi), plot_points=[50,50], aspect_ratio=[1,1,1])) A Cone:: sage: u, v = var('u,v') sage: parametric_plot3d([u*cos(v), u*sin(v), u], (u,-1,1), (v,0,2*pi+0.5), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([u*cos(v), u*sin(v), u], (u,-1,1), (v,0,2*pi+0.5), plot_points=[50,50])) A Paraboloid:: sage: u, v = var('u,v') sage: parametric_plot3d([u*cos(v), u*sin(v), u^2], (u,0,1), (v,0,2*pi+0.4), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([u*cos(v), u*sin(v), u**2], (u,0,1), (v,0,2*pi+0.4), plot_points=[50,50])) A Hyperboloid:: sage: u, v = var('u,v') sage: plot3d(u^2-v^2, (u,-1,1), (v,-1,1), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(plot3d(u**2-v**2, (u,-1,1), (v,-1,1), plot_points=[50,50])) A weird looking surface - like a Möbius band but also an O:: sage: u, v = var('u,v') sage: parametric_plot3d([sin(u)*cos(u)*log(u^2)*sin(v), (u^2)^(1/6)*(cos(u)^2)^(1/4)*cos(v), sin(v)], ....: (u,0.001,1), (v,-pi,pi+0.2), plot_points=[50,50]) Graphics3d Object .. PLOT:: u, v = var('u,v') sphinx_plot(parametric_plot3d([sin(u)*cos(u)*log(u**2)*sin(v), (u**2)**(1.0/6.0)*(cos(u)**2)**(0.25)*cos(v), sin(v)], (u,0.001,1), (v,-pi,pi+0.2), plot_points=[50,50])) A heart, but not a cardioid (for my wife):: sage: u, v = var('u,v') sage: p1 = parametric_plot3d([sin(u)*cos(u)*log(u^2)*v*(1-v)/2, ((u^6)^(1/20)*(cos(u)^2)^(1/4)-1/2)*v*(1-v), v^(0.5)], ....: (u,0.001,1), (v,0,1), plot_points=[70,70], color='red') sage: p2 = parametric_plot3d([-sin(u)*cos(u)*log(u^2)*v*(1-v)/2, ((u^6)^(1/20)*(cos(u)^2)^(1/4)-1/2)*v*(1-v), v^(0.5)], ....: (u, 0.001,1), (v,0,1), plot_points=[70,70], color='red') sage: show(p1+p2) .. PLOT:: u, v = var('u,v') p1 = parametric_plot3d([sin(u)*cos(u)*log(u**2)*v*(1-v)*0.5, ((u**6)**(1/20.0)*(cos(u)**2)**(0.25)-0.5)*v*(1-v), v**(0.5)], (u,0.001,1), (v,0,1), plot_points=[70,70], color='red') p2 = parametric_plot3d([-sin(u)*cos(u)*log(u**2)*v*(1-v)*0.5, ((u**6)**(1/20.0)*(cos(u)**2)**(0.25)-0.5)*v*(1-v), v**(0.5)], (u,0.001,1), (v,0,1), plot_points=[70,70], color='red') sphinx_plot(p1+p2) A Hyperhelicoidal:: sage: u = var("u") sage: v = var("v") sage: f_x = (sinh(v)*cos(3*u)) / (1+cosh(u)*cosh(v)) sage: f_y = (sinh(v)*sin(3*u)) / (1+cosh(u)*cosh(v)) sage: f_z = (cosh(v)*sinh(u)) / (1+cosh(u)*cosh(v)) sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), plot_points=[50,50], frame=False, color="red") Graphics3d Object .. PLOT:: u = var("u") v = var("v") f_x = (sinh(v)*cos(3*u)) / (1+cosh(u)*cosh(v)) f_y = (sinh(v)*sin(3*u)) / (1+cosh(u)*cosh(v)) f_z = (cosh(v)*sinh(u)) / (1+cosh(u)*cosh(v)) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), plot_points=[50,50], frame=False, color="red")) A Helicoid (lines through a helix, :wikipedia:`Helix`):: sage: u, v = var('u,v') sage: f_x = sinh(v) * sin(u) sage: f_y = -sinh(v) * cos(u) sage: f_z = 3 * u sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), plot_points=[50,50], frame=False, color="red") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = sinh(v) * sin(u) f_y = -sinh(v) * cos(u) f_z = 3 * u sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi,pi), (v,-pi,pi), plot_points=[50,50], frame=False, color="red")) Kuen's surface (http://virtualmathmuseum.org/Surface/kuen/kuen.html):: sage: f_x = (2*(cos(u) + u*sin(u))*sin(v))/(1+ u^2*sin(v)^2) sage: f_y = (2*(sin(u) - u*cos(u))*sin(v))/(1+ u^2*sin(v)^2) sage: f_z = log(tan(1/2 *v)) + (2*cos(v))/(1+ u^2*sin(v)^2) sage: parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0.01,pi-0.01), plot_points=[50,50], frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = (2.0*(cos(u)+u*sin(u))*sin(v)) / (1.0+u**2*sin(v)**2) f_y = (2.0*(sin(u)-u*cos(u))*sin(v)) / (1.0+u**2*sin(v)**2) f_z = log(tan(0.5 *v)) + (2*cos(v))/(1.0+u**2*sin(v)**2) sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,0,2*pi), (v,0.01,pi-0.01), plot_points=[50,50], frame=False, color="green")) A 5-pointed star:: sage: G1 = (abs(cos(u/4))^0.5+abs(sin(u/4))^0.5)^(-1/0.3) sage: G2 = (abs(cos(5*v/4))^1.7+abs(sin(5*v/4))^1.7)^(-1/0.1) sage: f_x = cos(u) * cos(v) * G1 * G2 sage: f_y = cos(u) * sin(v) * G1 * G2 sage: f_z = sin(u) * G1 sage: parametric_plot3d([f_x, f_y, f_z], (u,-pi/2,pi/2), (v,0,2*pi), plot_points=[50,50], frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') G1 = (abs(cos(u/4))**0.5+abs(sin(u/4))**0.5)**(-1/0.3) G2 = (abs(cos(5*v/4))**1.7+abs(sin(5*v/4))**1.7)**(-1/0.1) f_x = cos(u) * cos(v) * G1 * G2 f_y = cos(u) * sin(v) * G1 * G2 f_z = sin(u) * G1 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-pi/2,pi/2), (v,0,2*pi), plot_points=[50,50], frame=False, color="green")) A cool self-intersecting surface (Eppener surface?):: sage: f_x = u - u^3/3 + u*v^2 sage: f_y = v - v^3/3 + v*u^2 sage: f_z = u^2 - v^2 sage: parametric_plot3d([f_x, f_y, f_z], (u,-25,25), (v,-25,25), plot_points=[50,50], frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') f_x = u - u**3/3 + u*v**2 f_y = v - v**3/3 + v*u**2 f_z = u**2 - v**2 sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-25,25), (v,-25,25), plot_points=[50,50], frame=False, color="green")) The breather surface (:wikipedia:`Breather_surface`):: sage: K = sqrt(0.84) sage: G = (0.4*((K*cosh(0.4*u))^2 + (0.4*sin(K*v))^2)) sage: f_x = (2*K*cosh(0.4*u)*(-(K*cos(v)*cos(K*v)) - sin(v)*sin(K*v)))/G sage: f_y = (2*K*cosh(0.4*u)*(-(K*sin(v)*cos(K*v)) + cos(v)*sin(K*v)))/G sage: f_z = -u + (2*0.84*cosh(0.4*u)*sinh(0.4*u))/G sage: parametric_plot3d([f_x, f_y, f_z], (u,-13.2,13.2), (v,-37.4,37.4), plot_points=[90,90], frame=False, color="green") Graphics3d Object .. PLOT:: u, v = var('u,v') K = sqrt(0.84) G = (0.4*((K*cosh(0.4*u))**2 + (0.4*sin(K*v))**2)) f_x = (2*K*cosh(0.4*u)*(-(K*cos(v)*cos(K*v)) - sin(v)*sin(K*v)))/G f_y = (2*K*cosh(0.4*u)*(-(K*sin(v)*cos(K*v)) + cos(v)*sin(K*v)))/G f_z = -u + (2*0.84*cosh(0.4*u)*sinh(0.4*u))/G sphinx_plot(parametric_plot3d([f_x, f_y, f_z], (u,-13.2,13.2), (v,-37.4,37.4), plot_points=[90,90], frame=False, color="green")) TESTS:: sage: u, v = var('u,v') sage: plot3d(u^2-v^2, (u,-1,1), (u,-1,1)) Traceback (most recent call last): ... ValueError: range variables should be distinct, but there are duplicates From :trac:`2858`:: sage: parametric_plot3d((u,-u,v), (u,-10,10),(v,-10,10)) Graphics3d Object sage: f(u)=u; g(v)=v^2; parametric_plot3d((g,f,f), (-10,10),(-10,10)) Graphics3d Object From :trac:`5368`:: sage: x, y = var('x,y') sage: plot3d(x*y^2 - sin(x), (x,-1,1), (y,-1,1)) Graphics3d Object """ # TODO: # * Surface -- behavior of functions not defined everywhere -- see note above # * Iterative refinement # color_function -- (default: "automatic") how to determine the color of curves and surfaces # color_function_scaling -- (default: True) whether to scale the input to color_function # exclusions -- (default: "automatic") u points or (u,v) conditions to exclude. # (E.g., exclusions could be a function e = lambda u, v: False if u < v else True # exclusions_style -- (default: None) what to draw at excluded points # max_recursion -- (default: "automatic") maximum number of recursive subdivisions, # when ... # mesh -- (default: "automatic") how many mesh divisions in each direction to draw # mesh_functions -- (default: "automatic") how to determine the placement of mesh divisions # mesh_shading -- (default: None) how to shade regions between mesh divisions # plot_range -- (default: "automatic") range of values to include if is_Vector(f): f = tuple(f) if isinstance(f, (list, tuple)) and len(f) > 0 and isinstance( f[0], (list, tuple)): return sum([ parametric_plot3d(v, urange, vrange, plot_points=plot_points, **kwds) for v in f ]) if not isinstance(f, (tuple, list)) or len(f) != 3: raise ValueError("f must be a list, tuple, or vector of length 3") if vrange is None: if plot_points == "automatic": plot_points = 75 G = _parametric_plot3d_curve(f, urange, plot_points=plot_points, **kwds) else: if plot_points == "automatic": plot_points = [40, 40] G = _parametric_plot3d_surface(f, urange, vrange, plot_points=plot_points, boundary_style=boundary_style, **kwds) G._set_extra_kwds(kwds) return G
def __call__(self, v): """ Evaluate this quadratic form Q on a vector or matrix of elements coercible to the base ring of the quadratic form. If a vector is given then the output will be the ring element Q(`v`), but if a matrix is given then the output will be the quadratic form Q' which in matrix notation is given by: .. math:: Q' = v^t * Q * v. EXAMPLES:: ## Evaluate a quadratic form at a vector: ## -------------------------------------- sage: Q = QuadraticForm(QQ, 3, range(6)) sage: Q Quadratic form in 3 variables over Rational Field with coefficients: [ 0 1 2 ] [ * 3 4 ] [ * * 5 ] sage: Q([1,2,3]) 89 sage: Q([1,0,0]) 0 sage: Q([1,1,1]) 15 :: ## Evaluate a quadratic form using a column matrix: ## ------------------------------------------------ sage: Q = QuadraticForm(QQ, 2, range(1,4)) sage: A = Matrix(ZZ,2,2,[-1,0,0,1]) sage: Q(A) Quadratic form in 2 variables over Rational Field with coefficients: [ 1 -2 ] [ * 3 ] sage: Q([1,0]) 1 sage: type(Q([1,0])) <type 'sage.rings.rational.Rational'> sage: Q = QuadraticForm(QQ, 2, range(1,4)) sage: Q(matrix(2, [1,0])) Quadratic form in 1 variables over Rational Field with coefficients: [ 1 ] :: ## Simple 2x2 change of variables: ## ------------------------------- sage: Q = QuadraticForm(ZZ, 2, [1,0,1]) sage: Q Quadratic form in 2 variables over Integer Ring with coefficients: [ 1 0 ] [ * 1 ] sage: M = Matrix(ZZ, 2, 2, [1,1,0,1]) sage: M [1 1] [0 1] sage: Q(M) Quadratic form in 2 variables over Integer Ring with coefficients: [ 1 2 ] [ * 2 ] :: ## Some more tests: ## ---------------- sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q([1,2,3]) 14 sage: v = vector([1,2,3]) sage: Q(v) 14 sage: t = tuple([1,2,3]) sage: Q(v) 14 sage: M = Matrix(ZZ, 3, [1,2,3]) sage: Q(M) Quadratic form in 1 variables over Integer Ring with coefficients: [ 14 ] """ ## If we are passed a matrix A, return the quadratic form Q(A(x)) ## (In matrix notation: A^t * Q * A) n = self.dim() if is_Matrix(v): ## Check that v has the correct number of rows if v.nrows() != n: raise TypeError("Oops! The matrix must have " + str(n) + " rows. =(") ## Create the new quadratic form m = v.ncols() Q2 = QuadraticForm(self.base_ring(), m) return QFEvaluateMatrix(self, v, Q2) elif (is_Vector(v) or isinstance(v, (list, tuple))): ## Check the vector/tuple/list has the correct length if not (len(v) == n): raise TypeError("Oops! Your vector needs to have length " + str(n) + " .") ## TO DO: Check that the elements can be coerced into the base ring of Q -- on first elt. if len(v) > 0: try: x = self.base_ring()(v[0]) except Exception: raise TypeError("Oops! Your vector is not coercible to the base ring of the quadratic form... =(") ## Attempt to evaluate Q[v] return QFEvaluateVector(self, v) else: raise TypeError