Skip to content

Commit 00afc37

Browse files
committed
Merge branch 'feature/display_sky_luminances' into documentation
2 parents ef0a265 + 66047c7 commit 00afc37

File tree

2 files changed

+184
-51
lines changed

2 files changed

+184
-51
lines changed

src/alinea/astk/colormap.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# -*- python -*-
2+
#
3+
# Copyright 2015 INRIA - CIRAD - INRA
4+
#
5+
# Distributed under the Cecill-C License.
6+
# See accompanying file LICENSE.txt or copy at
7+
# http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
8+
#
9+
# WebSite : https://github.com/openalea-incubator/caribu
10+
#
11+
# ==============================================================================
12+
""" A colormap class
13+
copied from openalea.color.colormap because
14+
import openalea.color.colormap makes matplotlib
15+
with TkAgg frontend crash for some mysterious reason !!
16+
"""
17+
18+
from math import isnan
19+
20+
class ColorMap(object):
21+
"""A RGB color map, between 2 colors defined in HSV code
22+
23+
:Examples:
24+
25+
>>> minh,maxh = minandmax([height(i) for i in s2])
26+
>>> colormap = ColorMap(minh,maxh)
27+
>>> s3 = [ Shape(i.geometry, Material
28+
>>> (Color3(colormap(height(i))), 1), i.id)
29+
>>> for i in s2]
30+
31+
"""
32+
33+
def __init__(self, minval=0., maxval=1.):
34+
self.minval = float(minval)
35+
self.maxval = float(maxval)
36+
37+
def color(self, normedU):
38+
"""
39+
40+
:param normedU: todo
41+
42+
"""
43+
inter = 1 / 5.
44+
winter = int(normedU / inter)
45+
a = (normedU % inter) / inter
46+
b = 1 - a
47+
48+
if winter < 0:
49+
col = (self.coul2, self.coul2, self.coul1)
50+
elif winter == 0:
51+
col = (self.coul2, self.coul2 * b + self.coul1 * a, self.coul1)
52+
elif winter == 1:
53+
col = (self.coul2, self.coul1, self.coul1 * b + self.coul2 * a)
54+
elif winter == 2:
55+
col = (self.coul2 * b + self.coul1 * a, self.coul1, self.coul2)
56+
elif winter == 3:
57+
col = (self.coul1, self.coul1 * b + self.coul2 * a, self.coul2)
58+
elif winter > 3:
59+
col = (self.coul1, self.coul2, self.coul2)
60+
return (int(col[0]), int(col[1]), int(col[2]))
61+
62+
def greycolor(self, normedU):
63+
"""
64+
65+
:param normedU: todo
66+
:returns: todo
67+
"""
68+
return (int(255 * normedU), int(255 * normedU), int(255 * normedU))
69+
70+
def grey(self, u):
71+
"""
72+
:param u:
73+
:returns: todo
74+
"""
75+
return self.greycolor(self.normU(u))
76+
77+
def normU(self, u):
78+
"""
79+
:param u:
80+
:returns: todo
81+
"""
82+
if self.minval == self.maxval:
83+
return 0.5
84+
return (u - self.minval) / (self.maxval - self.minval)
85+
86+
def __call__(self, u, minval=0, maxval=1, coul1=80, coul2=20):
87+
self.coul1 = coul1
88+
self.coul2 = coul2
89+
self.minval = float(minval)
90+
self.maxval = float(maxval)
91+
return self.color(self.normU(u))
92+
93+
94+
def nan_to_zero(values):
95+
return [0 if isnan(x) else x for x in values]
96+
97+
98+
def jet_colors(values, minval=None, maxval=None):
99+
"""return jet colors associated to values after gamme normalisation
100+
101+
Args:
102+
values: (list of float) input values
103+
minval: (float) minimal value at lower bound of color range
104+
maxval: (float) maximal value at upper bound of color range
105+
106+
Returns:
107+
a list of (r, g, b) tuples
108+
"""
109+
110+
values = nan_to_zero(values)
111+
if minval is None:
112+
minval = min(values)
113+
if maxval is None:
114+
maxval = max(values)
115+
cmap = ColorMap()
116+
return map(lambda x: cmap(x, minval, maxval, 250., 20.), values)

src/alinea/astk/icosphere.py

Lines changed: 68 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@
3535
display_enable = False
3636

3737

38-
def display(vertices, faces, color=None, view=True):
38+
def display(vertices, faces, colors=None, view=True):
3939
"""3D display of a polyhedron with PlantGL
4040
4141
Args:
4242
vertices (list of tuples): list of 3D coordinates of polyhedron vertices
4343
faces (list of tuple): list of vertex indices defining the faces
44-
color: a (r,g,b) tuple defining color.
44+
color: a list of (r,g,b) tuple defining color.
4545
If None (default), default PlantGL material is used.
4646
view (bool): should the shape be displayed ?
4747
@@ -50,18 +50,23 @@ def display(vertices, faces, color=None, view=True):
5050
"""
5151
global display_enable
5252
if display_enable:
53-
if color is None:
53+
scene = pgl.Scene()
54+
if colors is None:
5455
shape = pgl.Shape(pgl.FaceSet(pointList=vertices, indexList=faces))
56+
scene += shape
5557
else:
56-
m = pgl.Material(pgl.Color3(*color))
57-
shape = pgl.Shape(pgl.FaceSet(pointList=vertices, indexList=faces),
58-
m)
58+
for i,face in enumerate(faces):
59+
vtx = [vertices[v] for v in face]
60+
idx = range(len(face))
61+
mat = pgl.Material(pgl.Color3(*colors[i]))
62+
shape = pgl.Shape(pgl.FaceSet(pointList=vtx, indexList=[idx]), mat)
63+
scene += shape
5964
if view:
60-
pgl.Viewer.display(shape)
65+
pgl.Viewer.display(scene)
6166
else:
6267
warnings.warn('PlantGL not installed: display is not enable!')
6368
shape = None
64-
return shape
69+
return scene
6570

6671

6772
def normed(point):
@@ -81,8 +86,11 @@ def norm(vector):
8186

8287
def spherical(points):
8388
""" zenital and azimutal coordinate of a list of points"""
84-
x, y, z = list(zip(*points))
85-
return numpy.arccos(z), numpy.arctan2(y, x)
89+
proj = [normed(p) for p in points]
90+
x, y, z = zip(*proj)
91+
theta = numpy.arccos(z)
92+
phi = numpy.arctan2(y, x)
93+
return zip(theta, phi)
8694

8795

8896
def rotation_matrix(axis, theta):
@@ -154,7 +162,7 @@ def icosahedron():
154162
vertices.append(normed((-t, 0, 1)))
155163

156164
# align to get second point on Z+
157-
theta, phi = list(zip(*spherical(vertices)))[1]
165+
theta, phi = spherical(vertices)[1]
158166
vertices = inverse_rotation(vertices, theta, phi)
159167

160168
# create 20 triangles of the icosahedron
@@ -206,11 +214,16 @@ def split_triangles(vertices, faces, tags=None):
206214
This is a python implementation of the C code found here:
207215
http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
208216
"""
217+
# copy input
218+
new_faces = [f for f in faces]
219+
new_vertices = [v for v in vertices]
220+
if tags is not None:
221+
new_tags = [t for t in tags]
209222
# cache is a (index_p1, index_p2): middle_point_index dict refering
210223
# to vertices
211224
cache = {}
212225
for i in range(len(faces)):
213-
face = faces.pop(0)
226+
face = new_faces.pop(0)
214227
v1, v2, v3 = face
215228
p1 = vertices[v1]
216229
p2 = vertices[v2]
@@ -221,35 +234,35 @@ def split_triangles(vertices, faces, tags=None):
221234
if ka in cache:
222235
va = cache[ka]
223236
else:
224-
vertices.append(normed(middle_point(p1, p2)))
225-
va = len(vertices) - 1
237+
new_vertices.append(normed(middle_point(p1, p2)))
238+
va = len(new_vertices) - 1
226239
cache.update({ka: va})
227240
if kb in cache:
228241
vb = cache[kb]
229242
else:
230-
vertices.append(normed(middle_point(p2, p3)))
231-
vb = len(vertices) - 1
243+
new_vertices.append(normed(middle_point(p2, p3)))
244+
vb = len(new_vertices) - 1
232245
cache.update({kb: vb})
233246
if kc in cache:
234247
vc = cache[kc]
235248
else:
236-
vertices.append(normed(middle_point(p1, p3)))
237-
vc = len(vertices) - 1
249+
new_vertices.append(normed(middle_point(p1, p3)))
250+
vc = len(new_vertices) - 1
238251
cache.update({kc: vc})
239252

240-
faces.append((v1, va, vc))
241-
faces.append((v2, vb, va))
242-
faces.append((v3, vc, vb))
243-
faces.append((va, vb, vc))
253+
new_faces.append((v1, va, vc))
254+
new_faces.append((v2, vb, va))
255+
new_faces.append((v3, vc, vb))
256+
new_faces.append((va, vb, vc))
244257

245258
if tags is not None:
246-
tag = tags.pop(0)
247-
tags.extend([tag] * 4)
259+
tag = new_tags.pop(0)
260+
new_tags.extend([tag] * 4)
248261

249262
if tags is None:
250-
return vertices, faces
263+
return new_vertices, new_faces
251264
else:
252-
return vertices, faces, tags
265+
return new_vertices, new_faces, new_tags
253266

254267

255268
def sorted_faces(center, face_indices, faces):
@@ -309,21 +322,27 @@ def star_split(vertices, faces, tags=None):
309322
of tags referencing the tag of the parent face
310323
"""
311324

325+
# copy input
326+
new_faces = [f for f in faces]
327+
new_vertices = [v for v in vertices]
328+
if tags is not None:
329+
new_tags = [t for t in tags]
330+
312331
for i in range(len(faces)):
313-
face = faces.pop(0)
332+
face = new_faces.pop(0)
314333
center = normed(centroid([vertices[p] for p in face]))
315-
icenter = len(vertices)
316-
vertices.append(center)
334+
icenter = len(new_vertices)
335+
new_vertices.append(center)
317336
for j in range(len(face) - 1):
318-
faces.append((face[j], face[j + 1], icenter))
319-
faces.append((face[-1], face[0], icenter))
337+
new_faces.append((face[j], face[j + 1], icenter))
338+
new_faces.append((face[-1], face[0], icenter))
320339
if tags is not None:
321-
tag = tags.pop(0)
322-
tags.extend([tag] * len(face))
340+
tag = new_tags.pop(0)
341+
new_tags.extend([tag] * len(face))
323342
if tags is None:
324-
return vertices, faces
343+
return new_vertices, new_faces
325344
else:
326-
return vertices, faces, tags
345+
return new_vertices, new_faces, new_tags
327346

328347

329348
def icosphere(iter_triangle=0, iter_star=0):
@@ -382,7 +401,7 @@ def turtle_dome(refine_level=3):
382401
383402
Args:
384403
refine_level (int): the level of refinement of the dual icosphere. By
385-
default 46 ^polygons are returned (refine_level=3).
404+
default 46 polygons are returned (refine_level=3).
386405
387406
For information, here are the number of faces obtained for the first ten
388407
refinement level: 0: 6, 1: 16, 2: 26, 3: 46, 4: 66, 5: 91, 6: 136,
@@ -446,36 +465,34 @@ def turtle_sectors(nb_sectors=46):
446465

447466

448467
def sample_faces(vertices, faces, iter=2, spheric=False):
449-
"""Generate a set of points that regularly sample the faces of a polyhedron
468+
"""Generate a set of points or spherical directions that regularly sample
469+
the faces of a polyhedron
450470
the number of sampling points is 6 * 4**iter or 5 * 4**iter
451471
452472
Args:
453473
vertices (list of tuples): list of 3D coordinates of polyhedron vertices
454474
faces (list of tuple): list of vertex indices defining the faces
455-
iter: the number of triangular interation to apply on the satr-split
475+
iter: the number of triangular iteration to apply on the star-split
456476
of the polyhedron. If None, face centers are returned
457-
speric (bool): if True, zenital and azimuth are returnd
477+
spheric (bool): if True, zenithal and azimuth are returned
458478
instead of points
459479
460480
Returns:
461-
a {face_index: [points]} dict
481+
a list of points or of (theta, phi) tuples and a list of tags
462482
"""
463-
464-
if iter is None:
465-
points = {i: [centroid([vertices[p] for p in face])] for i, face in
466-
enumerate(faces)}
467-
else:
468-
tags = list(range(len(faces)))
483+
tags = range(len(faces))
484+
if iter is not None:
469485
vertices, faces, tags = star_split(vertices, faces, tags)
470486
for i in range(iter):
471487
vertices, faces, tags = split_triangles(vertices, faces, tags)
472488

473-
points = {tag: [] for tag in set(tags)}
474-
for i, face in enumerate(faces):
475-
points[tags[i]].append(centroid([vertices[p] for p in face]))
489+
face_points = [[centroid([vertices[p] for p in face])] for face in faces]
476490

491+
points = reduce(lambda x, y: x + y, face_points)
492+
npt = map(len, face_points)
493+
tags = reduce(lambda x, y: x + y, [[t] * n for t, n in zip(tags, npt)])
477494
if spheric:
478-
points = {k:spherical(v) for k, v in points.items()}
495+
points = spherical(points)
479496

480-
return points
497+
return points, tags
481498

0 commit comments

Comments
 (0)