Example #1
0
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
Example #2
0
    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)
Example #3
0
    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
Example #4
0
    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
Example #5
0
    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)
Example #6
0
    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()
Example #8
0
 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)
Example #9
0
    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")
Example #11
0
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])
Example #12
0
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])
Example #14
0
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")
Example #15
0
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
Example #16
0
    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
Example #17
0
    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()
Example #19
0
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
Example #21
0
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
Example #22
0
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)]
Example #23
0
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)
        ]
Example #24
0
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