Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/plot/plot3d/texture.py
4036 views
1
r"""
2
Texture Support
3
4
This module provides texture/material support for 3D Graphics
5
objects and plotting. This is a very rough common interface for
6
Tachyon, x3d, and obj (mtl). See
7
:meth:`Texture <sage.plot.plot3d.texture.Texture>` and
8
:class:`Texture_class <sage.plot.plot3d.texture.Texture_class>`
9
for full details about options and use.
10
11
Initially, we have no textures set::
12
13
sage: sage.plot.plot3d.base.Graphics3d().texture_set()
14
set([])
15
16
However, one can access these textures in the following manner::
17
18
sage: G = tetrahedron(color='red') + tetrahedron(color='yellow') + tetrahedron(color='red', opacity=0.5)
19
sage: [t for t in G.texture_set() if t.color == colors.red] # we should have two red textures
20
[Texture(texture..., red, ff0000), Texture(texture..., red, ff0000)]
21
sage: [t for t in G.texture_set() if t.color == colors.yellow] # ...and one yellow
22
[Texture(texture..., yellow, ffff00)]
23
24
And the Texture objects keep track of all their data::
25
26
sage: T = tetrahedron(color='red', opacity=0.5)
27
sage: t = T.get_texture()
28
sage: t.opacity
29
0.500000000000000
30
sage: T # should be translucent
31
32
AUTHOR:
33
34
- Robert Bradshaw (2007-07-07) Initial version.
35
36
"""
37
from sage.structure.sage_object import SageObject
38
39
from sage.plot.colors import Color
40
41
42
uniq_c = 0
43
44
from sage.plot.colors import colors
45
46
def is_Texture(x):
47
r"""
48
Return whether ``x`` is an instance of ``Texture_class``.
49
50
EXAMPLES::
51
52
sage: from sage.plot.plot3d.texture import is_Texture, Texture
53
sage: t = Texture(0.5)
54
sage: is_Texture(t)
55
True
56
57
::
58
59
sage: is_Texture(4)
60
False
61
"""
62
return isinstance(x, Texture_class)
63
64
def Texture(id=None, **kwds):
65
r"""
66
Return a texture.
67
68
INPUT:
69
70
- ``id`` - a texture (optional, default: None), a dict, a color, a
71
str, a tuple, None or any other type acting as an ID. If ``id`` is
72
None, then it returns a unique texture object.
73
- ``texture`` - a texture
74
- ``color`` - tuple or str, (optional, default: (.4, .4, 1))
75
- ``opacity`` - number between 0 and 1 (optional, default: 1)
76
- ``ambient`` - number (optional, default: 0.5)
77
- ``diffuse`` - number (optional, default: 1)
78
- ``specular`` - number (optional, default: 0)
79
- ``shininess`` - number (optional, default: 1)
80
- ``name`` - str (optional, default: None)
81
- ``**kwds`` - other valid keywords
82
83
OUTPUT:
84
85
A texture object.
86
87
EXAMPLES:
88
89
Texture from integer ``id``::
90
91
sage: from sage.plot.plot3d.texture import Texture
92
sage: Texture(17)
93
Texture(17, 6666ff)
94
95
Texture from rational ``id``::
96
97
sage: Texture(3/4)
98
Texture(3/4, 6666ff)
99
100
Texture from a dict::
101
102
sage: Texture({'color':'orange','opacity':0.5})
103
Texture(texture..., orange, ffa500)
104
105
Texture from a color::
106
107
sage: c = Color('red')
108
sage: Texture(c)
109
Texture(texture..., ff0000)
110
111
Texture from a valid string color::
112
113
sage: Texture('red')
114
Texture(texture..., red, ff0000)
115
116
Texture from a non valid string color::
117
118
sage: Texture('redd')
119
Texture(redd, 6666ff)
120
121
Texture from a tuple::
122
123
sage: Texture((.2,.3,.4))
124
Texture(texture..., 334c66)
125
126
Textures using other keywords::
127
128
sage: Texture(specular=0.4)
129
Texture(texture..., 6666ff)
130
sage: Texture(diffuse=0.4)
131
Texture(texture..., 6666ff)
132
sage: Texture(shininess=0.3)
133
Texture(texture..., 6666ff)
134
sage: Texture(ambiant=0.7)
135
Texture(texture..., 6666ff)
136
"""
137
if isinstance(id, Texture_class):
138
return id
139
if 'texture' in kwds:
140
t = kwds['texture']
141
if is_Texture(t):
142
return t
143
else:
144
raise TypeError("texture keyword must be a texture object")
145
if isinstance(id, dict):
146
kwds = id
147
if 'rgbcolor' in kwds:
148
kwds['color'] = kwds['rgbcolor']
149
id = None
150
elif isinstance(id, Color):
151
kwds['color'] = id.rgb()
152
id = None
153
elif isinstance(id, str) and id in colors:
154
kwds['color'] = id
155
id = None
156
elif isinstance(id, tuple):
157
kwds['color'] = id
158
id = None
159
if id is None:
160
global uniq_c
161
uniq_c += 1
162
id = "texture%s" % uniq_c
163
return Texture_class(id, **kwds)
164
165
def parse_color(info, base=None):
166
r"""
167
Parses the color.
168
169
It transforms a valid color string into a color object and
170
a color object into an RBG tuple of length 3. Otherwise,
171
it multiplies the info by the base color.
172
173
INPUT:
174
175
- ``info`` - color, valid color str or number
176
- ``base`` - tuple of length 3 (optional, default: None)
177
178
OUTPUT:
179
180
A tuple or color.
181
182
EXAMPLES:
183
184
From a color::
185
186
sage: from sage.plot.plot3d.texture import parse_color
187
sage: c = Color('red')
188
sage: parse_color(c)
189
(1.0, 0.0, 0.0)
190
191
From a valid color str::
192
193
sage: parse_color('red')
194
RGB color (1.0, 0.0, 0.0)
195
sage: parse_color('#ff0000')
196
RGB color (1.0, 0.0, 0.0)
197
198
From a non valid color str::
199
200
sage: parse_color('redd')
201
Traceback (most recent call last):
202
...
203
ValueError: unknown color 'redd'
204
205
From an info and a base::
206
207
sage: opacity = 10
208
sage: parse_color(opacity, base=(.2,.3,.4))
209
(2.0, 3.0, 4.0)
210
"""
211
if isinstance(info, Color):
212
return info.rgb()
213
elif isinstance(info, str):
214
try:
215
return Color(info)
216
except KeyError:
217
raise ValueError("unknown color '%s'"%info)
218
else:
219
r, g, b = base
220
# We don't want to lose the data when we split it into its respective components.
221
if not r: r = 1e-5
222
if not g: g = 1e-5
223
if not b: b = 1e-5
224
return (float(info*r), float(info*g), float(info*b))
225
226
227
class Texture_class(SageObject):
228
r"""
229
Construction of a texture.
230
231
See documentation of :meth:`Texture <sage.plot.plot3d.texture.Texture>`
232
for more details and examples.
233
234
EXAMPLES:
235
236
We create a translucent texture::
237
238
sage: from sage.plot.plot3d.texture import Texture
239
sage: t = Texture(opacity=0.6)
240
sage: t
241
Texture(texture..., 6666ff)
242
sage: t.opacity
243
0.600000000000000
244
sage: t.jmol_str('obj')
245
'color obj translucent 0.4 [102,102,255]'
246
sage: t.mtl_str()
247
'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1\nd 0.600000000000000'
248
sage: t.x3d_str()
249
"<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance>"
250
"""
251
def __init__(self, id, color=(.4, .4, 1), opacity=1, ambient=0.5, diffuse=1, specular=0, shininess=1, name=None, **kwds):
252
r"""
253
Construction of a texture.
254
255
See documentation of :meth:`Texture <sage.plot.plot3d.texture.Texture>`
256
for more details and examples.
257
258
EXAMPLES::
259
260
sage: from sage.plot.plot3d.texture import Texture_class
261
sage: Texture_class(3, opacity=0.6)
262
Texture(3, 6666ff)
263
"""
264
self.id = id
265
if name is None and isinstance(color, str):
266
name = color
267
self.name = name
268
269
if not isinstance(color, tuple):
270
color = parse_color(color)
271
else:
272
if len(color) == 4:
273
opacity = color[3]
274
color = (float(color[0]), float(color[1]), float(color[2]))
275
276
self.color = color
277
self.opacity = opacity
278
self.shininess = shininess
279
280
if not isinstance(ambient, tuple):
281
ambient = parse_color(ambient, color)
282
self.ambient = ambient
283
284
if not isinstance(diffuse, tuple):
285
diffuse = parse_color(diffuse, color)
286
self.diffuse = diffuse
287
288
if not isinstance(specular, tuple):
289
specular = parse_color(specular, color)
290
self.specular = specular
291
292
def _repr_(self):
293
"""
294
Gives string representation of the Texture object.
295
296
EXAMPLES::
297
298
sage: from sage.plot.plot3d.texture import Texture
299
sage: Texture('yellow') #indirect doctest
300
Texture(texture..., yellow, ffff00)
301
sage: Texture((1,1,0), opacity=.5)
302
Texture(texture..., ffff00)
303
"""
304
if self.name is not None:
305
return "Texture(%s, %s, %s)" % (self.id, self.name, self.hex_rgb())
306
else:
307
return "Texture(%s, %s)" % (self.id, self.hex_rgb())
308
309
def hex_rgb(self):
310
"""
311
EXAMPLES::
312
313
sage: from sage.plot.plot3d.texture import Texture
314
sage: Texture('red').hex_rgb()
315
'ff0000'
316
sage: Texture((1, .5, 0)).hex_rgb()
317
'ff7f00'
318
"""
319
return "%02x%02x%02x" % tuple(int(255*s) for s in self.color)
320
321
def tachyon_str(self):
322
r"""
323
Converts Texture object to string suitable for Tachyon ray tracer.
324
325
EXAMPLES::
326
327
sage: from sage.plot.plot3d.texture import Texture
328
sage: t = Texture(opacity=0.6)
329
sage: t.tachyon_str()
330
'Texdef texture...\n Ambient 0.333333333333 Diffuse 0.666666666667 Specular 0.0 Opacity 0.600000000000000\n Color 0.4 0.4 1.0\n TexFunc 0'
331
"""
332
total_color = float(sum(self.ambient) + sum(self.diffuse) + sum(self.specular))
333
if total_color == 0:
334
total_color = 1
335
return "Texdef %s\n" % self.id + \
336
" Ambient %s Diffuse %s Specular %s Opacity %s\n" % \
337
(sum(self.ambient)/total_color,
338
sum(self.diffuse)/total_color,
339
sum(self.specular)/total_color,
340
self.opacity) + \
341
" Color %s %s %s\n" % (self.color[0], self.color[1], self.color[2]) + \
342
" TexFunc 0"
343
344
def x3d_str(self):
345
r"""
346
Converts Texture object to string suitable for x3d.
347
348
EXAMPLES::
349
350
sage: from sage.plot.plot3d.texture import Texture
351
sage: t = Texture(opacity=0.6)
352
sage: t.x3d_str()
353
"<Appearance><Material diffuseColor='0.4 0.4 1.0' shininess='1' specularColor='0.0 0.0 0.0'/></Appearance>"
354
"""
355
return "<Appearance><Material diffuseColor='%s %s %s' shininess='%s' specularColor='%s %s %s'/></Appearance>" % \
356
(self.color[0], self.color[1], self.color[2], self.shininess, self.specular[0], self.specular[0], self.specular[0])
357
358
def mtl_str(self):
359
r"""
360
Converts Texture object to string suitable for mtl output.
361
362
EXAMPLES::
363
364
sage: from sage.plot.plot3d.texture import Texture
365
sage: t = Texture(opacity=0.6)
366
sage: t.mtl_str()
367
'newmtl texture...\nKa 0.2 0.2 0.5\nKd 0.4 0.4 1.0\nKs 0.0 0.0 0.0\nillum 1\nNs 1\nd 0.600000000000000'
368
"""
369
return "\n".join(["newmtl %s" % self.id,
370
"Ka %s %s %s" % self.ambient,
371
"Kd %s %s %s" % self.diffuse,
372
"Ks %s %s %s" % self.specular,
373
"illum %s" % (2 if sum(self.specular) > 0 else 1),
374
"Ns %s" % self.shininess,
375
"d %s" % self.opacity, ])
376
377
def jmol_str(self, obj):
378
r"""
379
Converts Texture object to string suitable for Jmol applet.
380
381
INPUT:
382
383
- ``obj`` - str
384
385
EXAMPLES::
386
387
sage: from sage.plot.plot3d.texture import Texture
388
sage: t = Texture(opacity=0.6)
389
sage: t.jmol_str('obj')
390
'color obj translucent 0.4 [102,102,255]'
391
392
::
393
394
sage: sum([dodecahedron(center=[2.5*x, 0, 0], color=(1, 0, 0, x/10)) for x in range(11)]).show(aspect_ratio=[1,1,1], frame=False, zoom=2)
395
"""
396
translucent = "translucent %s" % float(1-self.opacity) if self.opacity < 1 else ""
397
return "color %s %s [%s,%s,%s]" % (obj, translucent,
398
int(255*self.color[0]), int(255*self.color[1]), int(255*self.color[2]))
399
400
401