Indexed face sets#

Graphics3D object that consists of a list of polygons, also used for triangulations of other objects.

Usually these objects are not created directly by users.

AUTHORS:

  • Robert Bradshaw (2007-08-26): initial version

  • Robert Bradshaw (2007-08-28): significant optimizations

Todo

Smooth triangles using vertex normals

class sage.plot.plot3d.index_face_set.EdgeIter#

Bases: object

A class for iteration over edges

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: len(list(S.edges())) == 12   # indirect doctest
True
class sage.plot.plot3d.index_face_set.FaceIter#

Bases: object

A class for iteration over faces

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: len(list(S.faces())) == 6   # indirect doctest
True
class sage.plot.plot3d.index_face_set.IndexFaceSet#

Bases: PrimitiveObject

Graphics3D object that consists of a list of polygons, also used for triangulations of other objects.

Polygons (mostly triangles and quadrilaterals) are stored in the c struct face_c (see transform.pyx). Rather than storing the points directly for each polygon, each face consists a list of pointers into a common list of points which are basically triples of doubles in a point_c.

Moreover, each face has an attribute color which is used to store color information when faces are colored. The red/green/blue components are then available as floats between 0 and 1 using color.r,color.g,color.b.

Usually these objects are not created directly by users.

EXAMPLES:

sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
sage: S = IndexFaceSet([[(1,0,0),(0,1,0),(0,0,1)], [(1,0,0),(0,1,0),(0,0,0)]])
sage: S.face_list()
[[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)],
 [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0)]]
sage: S.vertex_list()
[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0), (0.0, 0.0, 0.0)]

sage: def make_face(n): return [(0,0,n),(0,1,n),(1,1,n),(1,0,n)]
sage: S = IndexFaceSet([make_face(n) for n in range(10)])
sage: S.show()

sage: point_list = [(1,0,0),(0,1,0)] + [(0,0,n) for n in range(10)]
sage: face_list = [[0,1,n] for n in range(2,10)]
sage: S = IndexFaceSet(face_list, point_list, color='red')
sage: S.face_list()
[[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0)],
 [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)],
 [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 2.0)],
 [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 3.0)],
 [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 4.0)],
 [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 5.0)],
 [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 6.0)],
 [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 7.0)]]
sage: S.show()

A simple example of colored IndexFaceSet (github issue #12212):

sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
sage: from sage.plot.plot3d.texture import Texture
sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
sage: col = rainbow(10, 'rgbtuple')
sage: t_list = [Texture(col[i]) for i in range(10)]
sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
sage: S.show(viewer='tachyon')
add_condition(condition, N=100, eps=1e-06)#

Cut the surface according to the given condition.

This allows to take the intersection of the surface with a domain in 3-space, in such a way that the result has a smooth boundary.

INPUT:

  • condition – boolean function on ambient space, that defines the domain

  • N – max number of steps used by the bisection method (default: 100) to cut the boundary triangles that are not entirely within the domain.

  • eps – target accuracy in the intersection (default: 1.0e-6)

OUTPUT:

an IndexFaceSet

This will contain both triangular and quadrilateral faces.

EXAMPLES:

sage: var('x,y,z')                                                          # needs sage.symbolic
(x, y, z)
sage: P = implicit_plot3d(z-x*y,(-2,2),(-2,2),(-2,2))                       # needs sage.symbolic
sage: def condi(x,y,z):
....:     return bool(x*x+y*y+z*z <= Integer(1))
sage: R = P.add_condition(condi, 20); R                                     # needs sage.symbolic
Graphics3d Object
../../../_images/index_face_set-1.svg

An example with colors:

sage: def condi(x,y,z):
....:     return bool(x*x+y*y <= 1.1)
sage: cm = colormaps.hsv
sage: cf = lambda x,y,z: float(x+y) % 1
sage: P = implicit_plot3d(x**2+y**2+z**2-1-x**2*z+y**2*z,                   # needs sage.symbolic
....:                     (-2,2),(-2,2),(-2,2),color=(cm,cf))
sage: R = P.add_condition(condi,40); R                                      # needs sage.symbolic
Graphics3d Object
../../../_images/index_face_set-2.svg

An example with transparency:

sage: P = implicit_plot3d(x**4+y**4+z**2-4, (x,-2,2), (y,-2,2), (z,-2,2),   # needs sage.symbolic
....:                     alpha=0.3)
sage: def cut(a,b,c):
....:     return a*a+c*c > 2
sage: Q = P.add_condition(cut,40); Q                                        # needs sage.symbolic
Graphics3d Object
../../../_images/index_face_set-3.svg

A sombrero with quadrilaterals:

sage: P = plot3d(-sin(2*x*x+2*y*y)*exp(-x*x-y*y), (x,-2,2), (y,-2,2),       # needs sage.symbolic
....:            color='gold')
sage: def cut(x,y,z):
....:     return x*x+y*y < 1
sage: Q = P.add_condition(cut);Q                                            # needs sage.symbolic
Graphics3d Object
../../../_images/index_face_set-4.svg

Todo

  • Use a dichotomy to search for the place where to cut,

  • Compute the cut only once for each edge.

bounding_box()#

Calculate the bounding box for the vertices in this object (ignoring infinite or NaN coordinates).

OUTPUT:

a tuple ( (low_x, low_y, low_z), (high_x, high_y, high_z)), which gives the coordinates of opposite corners of the bounding box.

EXAMPLES:

sage: x,y = var('x,y')                                                      # needs sage.symbolic
sage: p = plot3d(sqrt(sin(x)*sin(y)), (x,0,2*pi), (y,0,2*pi))               # needs sage.symbolic
sage: p.bounding_box()                                                      # needs sage.symbolic
((0.0, 0.0, 0.0), (6.283185307179586, 6.283185307179586, 0.9991889981715697))
dual(**kwds)#

Return the dual.

EXAMPLES:

sage: S = cube()
sage: T = S.dual()
sage: len(T.vertex_list())
6
edge_list()#

Return the list of edges.

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: S.edge_list()[0]
((1.0, -2.0, 3.0), (1.0, 2.0, 3.0))
edges()#

An iterator over the edges.

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: list(S.edges())[0]
((1.0, -2.0, 3.0), (1.0, 2.0, 3.0))
face_list(render_params=None)#

Return the list of faces.

Every face is given as a tuple of vertices.

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: S.face_list(S.default_render_params())[0]
[(1.0, 2.0, 3.0), (-1.0, 2.0, 3.0), (-1.0, -2.0, 3.0), (1.0, -2.0, 3.0)]
faces()#

An iterator over the faces.

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: list(S.faces()) == S.face_list()
True
has_local_colors()#

Return True if and only if every face has an individual color.

EXAMPLES:

sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
sage: from sage.plot.plot3d.texture import Texture
sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
sage: col = rainbow(10, 'rgbtuple')
sage: t_list=[Texture(col[i]) for i in range(10)]
sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
sage: S.has_local_colors()
True

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: S.has_local_colors()
False
index_faces()#

Return the list over all faces of the indices of the vertices.

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: S.index_faces()
[[0, 1, 2, 3],
 [0, 4, 5, 1],
 [0, 3, 6, 4],
 [5, 4, 6, 7],
 [6, 3, 2, 7],
 [2, 1, 5, 7]]
index_faces_with_colors()#

Return the list over all faces of (indices of the vertices, color).

This only works if every face has its own color.

EXAMPLES:

A simple colored one:

sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
sage: from sage.plot.plot3d.texture import Texture
sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
sage: col = rainbow(10, 'rgbtuple')
sage: t_list = [Texture(col[i]) for i in range(10)]
sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
sage: S.index_faces_with_colors()
[([0, 4, 5], '#ff0000'),
 ([3, 4, 5], '#ff9900'),
 ([2, 3, 4], '#cbff00'),
 ([1, 3, 5], '#33ff00')]

When the texture is global, an error is raised:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: S.index_faces_with_colors()
Traceback (most recent call last):
...
ValueError: the texture is global
is_enclosed()#

Whether or not it is necessary to render the back sides of the polygons.

One is assuming, of course, that they have the correct orientation.

This is may be passed in on construction. It is also calculated in sage.plot.plot3d.parametric_surface.ParametricSurface by verifying the opposite edges of the rendered domain either line up or are pinched together.

EXAMPLES:

sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
sage: IndexFaceSet([[(0,0,1),(0,1,0),(1,0,0)]]).is_enclosed()
False
jmol_repr(render_params)#

Return a jmol representation for self.

json_repr(render_params)#

Return a json representation for self.

obj_repr(render_params)#

Return an obj representation for self.

partition(f)#

Partition the faces of self.

The partition is done according to the value of a map \(f: \RR^3 \rightarrow \ZZ\) applied to the center of each face.

INPUT:

  • \(f\) – a function from \(\RR^3\) to \(\ZZ\)

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: len(S.partition(lambda x,y,z: floor(x+y+z)))
6
sticker(face_list, width, hover, **kwds)#

Return a sticker on the chosen faces.

stickers(colors, width, hover)#

Return a group of IndexFaceSets.

INPUT:

  • colors – list of colors/textures to use (in cyclic order)

  • width – offset perpendicular into the edge (to create a border) may also be negative

  • hover – offset normal to the face (usually have to float above the original surface so it shows, typically this value is very small compared to the actual object

OUTPUT:

Graphics3dGroup of stickers

EXAMPLES:

sage: from sage.plot.plot3d.shapes import Box
sage: B = Box(.5,.4,.3, color='black')
sage: S = B.stickers(['red','yellow','blue'], 0.1, 0.05)
sage: S.show()
sage: (S+B).show()
stl_binary_repr(render_params)#

Return data for STL (STereoLithography) representation of the surface.

The STL binary representation is a list of binary strings, one for each triangle.

EXAMPLES:

sage: G = sphere()
sage: data = G.stl_binary_repr(G.default_render_params()); len(data)
1368
tachyon_repr(render_params)#

Return a tachyon object for self.

EXAMPLES:

A basic test with a triangle:

sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)])
sage: s = G.tachyon_repr(G.default_render_params()); s
['TRI V0 0 0 1 V1 1 1 1 V2 2 0 1', ...]

A simple colored one:

sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
sage: from sage.plot.plot3d.texture import Texture
sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
sage: col = rainbow(10, 'rgbtuple')
sage: t_list = [Texture(col[i]) for i in range(10)]
sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
sage: S.tachyon_repr(S.default_render_params())
['TRI V0 2 0 0 V1 1 0 1 V2 1 1 0',
'TEXTURE... AMBIENT 0.3 DIFFUSE 0.7 SPECULAR 0 OPACITY 1.0... COLOR 1 0 0 ... TEXFUNC 0',...]
threejs_repr(render_params)#

Return representation of the surface suitable for plotting with three.js.

EXAMPLES:

A simple triangle:

sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)])
sage: G.threejs_repr(G.default_render_params())
[('surface',
  {'color': '#0000ff',
   'faces': [[0, 1, 2]],
   'opacity': 1.0,
   'vertices': [{'x': 0.0, 'y': 0.0, 'z': 1.0},
    {'x': 1.0, 'y': 1.0, 'z': 1.0},
    {'x': 2.0, 'y': 0.0, 'z': 1.0}]})]

The same but with more options applied:

sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)], color='red', opacity=0.5,
....:             render_order=2, threejs_flat_shading=True,
....:             single_side=True, mesh=True, thickness=10, depth_write=True)
sage: G.threejs_repr(G.default_render_params())
[('surface',
  {'color': '#ff0000',
   'depthWrite': True,
   'faces': [[0, 1, 2]],
   'linewidth': 10.0,
   'opacity': 0.5,
   'renderOrder': 2.0,
   'showMeshGrid': True,
   'singleSide': True,
   'useFlatShading': True,
   'vertices': [{'x': 0.0, 'y': 0.0, 'z': 1.0},
    {'x': 1.0, 'y': 1.0, 'z': 1.0},
    {'x': 2.0, 'y': 0.0, 'z': 1.0}]})]
vertex_list()#

Return the list of vertices.

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = polygon([(0,0,1), (1,1,1), (2,0,1)])
sage: S.vertex_list()[0]
(0.0, 0.0, 1.0)
vertices()#

An iterator over the vertices.

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Cone(1,1)
sage: list(S.vertices()) == S.vertex_list()
True
x3d_geometry()#

Return the x3d data.

EXAMPLES:

A basic test with a triangle:

sage: G = polygon([(0,0,1), (1,1,1), (2,0,1)])
sage: print(G.x3d_geometry())

<IndexedFaceSet coordIndex='0,1,2,-1'>
  <Coordinate point='0.0 0.0 1.0,1.0 1.0 1.0,2.0 0.0 1.0'/>
</IndexedFaceSet>

A simple colored one:

sage: from sage.plot.plot3d.index_face_set import IndexFaceSet
sage: from sage.plot.plot3d.texture import Texture
sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)]
sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]]
sage: col = rainbow(10, 'rgbtuple')
sage: t_list = [Texture(col[i]) for i in range(10)]
sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list)
sage: print(S.x3d_geometry())

<IndexedFaceSet solid='False' colorPerVertex='False' coordIndex='0,4,5,-1,3,4,5,-1,2,3,4,-1,1,3,5,-1'>
  <Coordinate point='2.0 0.0 0.0,0.0 2.0 0.0,0.0 0.0 2.0,0.0 1.0 1.0,1.0 0.0 1.0,1.0 1.0 0.0'/>
  <Color color='1.0 0.0 0.0,1.0 0.6000000000000001 0.0,0.7999999999999998 1.0 0.0,0.20000000000000018 1.0 0.0' />
</IndexedFaceSet>
class sage.plot.plot3d.index_face_set.VertexIter#

Bases: object

A class for iteration over vertices

EXAMPLES:

sage: from sage.plot.plot3d.shapes import *
sage: S = Box(1,2,3)
sage: len(list(S.vertices())) == 8   # indirect doctest
True
sage.plot.plot3d.index_face_set.cut_edge_by_bisection(pointa, pointb, condition, eps=1e-06, N=100)#

Cut an intersecting edge using the bisection method.

Given two points (pointa and pointb) and a condition (boolean function), this calculates the position at the edge (defined by both points) where the boolean condition switches its value.

INPUT:

  • pointa, pointb – two points in 3-dimensional space

  • N – max number of steps in the bisection method (default: 100) to cut the boundary triangles that are not entirely within the domain.

  • eps – target accuracy in the intersection (default: 1.0e-6)

OUTPUT:

intersection of the edge defined by pointa and pointb, and condition.

EXAMPLES:

sage: from sage.plot.plot3d.index_face_set import cut_edge_by_bisection
sage: cut_edge_by_bisection((0.0,0.0,0.0), (1.0,1.0,0.0),
....:                       lambda x,y,z: x**2+y**2+z**2 < 1, eps=1.0E-12)
(0.7071067811864395, 0.7071067811864395, 0.0)
sage.plot.plot3d.index_face_set.midpoint(pointa, pointb, w)#

Return the weighted mean of two points in 3-space.

INPUT:

  • pointa, pointb – two points in 3-dimensional space

  • w – a real weight between 0 and 1.

If the weight is zero, the result is pointb. If the weight is one, the result is pointa.

EXAMPLES:

sage: from sage.plot.plot3d.index_face_set import midpoint
sage: midpoint((1,2,3),(4,4,4),0.8)
(1.60000000000000, 2.40000000000000, 3.20000000000000)
sage.plot.plot3d.index_face_set.sticker(face, width, hover)#

Return a sticker over the given face.