""" Convert a PlantGL scene to k3d.
3D visualisation widgets in Jupyter.
"""
from __future__ import absolute_import
from openalea.plantgl.all import *
import matplotlib
import numpy as np
import k3d
import six
from six.moves import zip
[docs]
def rgb2hex(r,g,b):
"""Convert an RGB value to an hex value"""
return (r << 16) | (g << 8) | b
[docs]
def tomesh(obj, d=None, side='front'):
"""Return a mesh from a geometry object or scene"""
isCurve = False
if isinstance(obj, Scene):
return group_meshes_by_color(obj, side=side)
if obj.isACurve():
isCurve = True
if d is None:
d = Tesselator() if not isCurve else Discretizer()
obj.apply(d)
if isCurve:
pts = [(pt.x, pt.y, pt.z) for pt in list(d.result.pointList)]
mesh = k3d.line(pts, shader='mesh')
else:
idl = [tuple(index) for index in list(d.discretization.indexList)]
pts = [(pt.x, pt.y, pt.z) for pt in list(d.discretization.pointList)]
mesh = k3d.mesh(vertices=pts, indices=idl, side=side)
return mesh
[docs]
def curve2mesh(crv, property=None):
"""Return a mesh from a curve"""
d = Discretizer()
indices, vertices, colors, attribute=[], [], [], []
colordict={}
count=-1
offset=0
for obj in crv:
status = obj.apply(d)
pts = [(pt.x, pt.y, pt.z) for pt in list(d.result.pointList)]
vertices.extend(pts)
color = obj.appearance.ambient
color = (color.red, color.green, color.blue)
if color not in colordict:
count += 1
colordict[color] = count
offset += len(pts)
attribute.extend([colordict[color]]*len(pts))
colors=np.array(list(colordict.keys()))/255.
if property is not None:
property = np.repeat(np.array(property), [3]*len(property))
mesh = k3d.line(vertices, shader='mesh', attribute=property, color_map=k3d.basic_color_maps.Jet, color_range=[min(property), max(property)])
elif len(colors) == 1:
colorhex = int(matplotlib.colors.rgb2hex(colors[0])[1:], 16)
mesh = k3d.line(vertices, shader='mesh')
mesh.color=colorhex
else:
color_map = list(zip(list(np.array(list(colordict.values())) /
float(max(colordict.values()))),
colors[:,0],
colors[:,1],
colors[:,2]))
color_map.sort()
attribute = list(np.array(attribute)/float(max(attribute)))
mesh = k3d.line(vertices, shader='mesh', attribute=attribute, color_map=color_map)
return mesh
[docs]
def scene2mesh(scene, property=None, side='front'):
"""Return a mesh from a scene"""
d = Tesselator()
indices, vertices, colors, attribute, color_triangles = [], [], [], [], []
colordict={}
count=-1
offset=0
opacity = 1.0
curves, texts, meshes = [], [], []
for obj in scene:
opacity = 1.0
if isinstance(obj.geometry, Text):
pos = obj.geometry.position
texts.append(k3d.text(obj.geometry.string, [pos.x, pos.y, pos.z], label_box=False, color=0xaaaaaa))
continue
if obj.geometry.isACurve():
curves.append(obj)
continue
obj.geometry.apply(d)
idl = np.array([tuple(index) for index in list(d.discretization.indexList)])+offset
pts = [(pt.x, pt.y, pt.z) for pt in list(d.discretization.pointList)]
if d.discretization.colorList: # if a color list is defined, use it
col_list = d.discretization.colorList
for col in col_list:
hex_val = rgb2hex(col.red, col.green, col.blue) # get the hex code of rgb color
color_triangles.append(hex_val)
vertices.extend(pts)
color = obj.appearance.ambient
color = (color.red, color.green, color.blue)
opacity = 1 - obj.appearance.transparency
if color not in colordict:
count += 1
colordict[color] = count
offset += len(pts)
attribute.extend([colordict[color]]*len(pts))
indices.extend(idl.tolist())
colors=np.array(list(colordict.keys()))/255.
if property is not None:
property = np.repeat(np.array(property), [3]*len(property))
mesh = k3d.mesh(vertices=vertices, indices=indices, attribute=property,
color_map=k3d.basic_color_maps.Jet,
color_range=[min(property), max(property)],
side=side, opacity=opacity)
if len(color_triangles) > 0: # Color by triangles
color_triangles = np.repeat(color_triangles, 3).astype(np.uint32) # colors argument is by points so repeat for triangle
mesh = k3d.mesh(vertices=vertices, indices=indices, colors=color_triangles, side=side, opacity=opacity)
elif len(colors) == 1:
colorhex = int(matplotlib.colors.rgb2hex(colors[0])[1:], 16)
mesh = k3d.mesh(vertices=vertices, indices=indices, side=side, opacity=opacity)
mesh.color=colorhex
else:
try:
color_map = list(zip(list(np.array(list(colordict.values())) /
float(max(colordict.values()))),
colors[:,0],
colors[:,1],
colors[:,2]))
color_map.sort()
attribute = list(np.array(attribute)/float(max(attribute)))
mesh = k3d.mesh(vertices=vertices,
indices=indices,
attribute=attribute,
color_map=color_map,
side=side,
opacity=opacity)
except ValueError:
mesh = k3d.mesh(vertices=vertices,
indices=indices,
attribute=attribute,
side=side,
opacity=opacity)
meshes = [mesh]
if curves:
meshes.extend([curve2mesh([crv]) for crv in curves])
print("Display %d curves"%len(curves))
if texts:
meshes.extend(texts)
return meshes
[docs]
def group_meshes_by_color(scene, side='front'):
""" Create one mesh by objects sharing the same color.
"""
group_color = {}
texts = []
for obj in scene:
if isinstance(obj.geometry, Text):
pos = obj.geometry.position
texts.append(k3d.text(obj.geometry.string, [pos.x, pos.y, pos.z], label_box=False, color=0xaaaaaa))
continue
color = obj.appearance.ambient
color = (color.red, color.green, color.blue)
group_color.setdefault(color, []).append(obj)
curves = {}
k_to_pop = []
for k, obj in group_color.items():
if obj[0].geometry.isACurve():
curves[k] = obj
k_to_pop.append(k)
for k in k_to_pop:
group_color.pop(k)
meshes_scene = [scene2mesh(objects, side=side)[0] for objects in group_color.values()]
# only one curve element in group_color - so take that element to split its lines
if curves:
meshes_crv = [curve2mesh([obj]) for obj in list(curves.values())[0]]
meshes_scene.extend(meshes_crv)
if texts:
meshes_scene.extend(texts)
return meshes_scene
[docs]
def PlantGL(pglobject, plot=None, group_by_color=True, property=None, side='front'):
"""Return a k3d plot from PlantGL shape, geometry and scene objects"""
if plot is None:
plot = k3d.plot()
if isinstance(pglobject, Geometry):
mesh = tomesh(pglobject, side=side)
plot += mesh
elif isinstance(pglobject, Shape):
mesh = tomesh(pglobject.geometry, side=side)
mesh.color = pglobject.appearance.ambient.toUint()
plot += mesh
elif isinstance(pglobject, Scene):
if group_by_color:
meshes = group_meshes_by_color(pglobject, side=side)
for mesh in meshes:
plot += mesh
else:
meshes = scene2mesh(pglobject, property, side=side)
for mesh in meshes:
plot += mesh
plot.lighting = 3
#plot.colorbar_object_id = randint(0, 1000)
return plot
[docs]
def mtg2mesh(g, property_name):
"""Return a mesh from an MTG object depending on a specific property"""
d = Tesselator()
geometry = g.property('geometry')
prop = g.property(property_name)
vertices, indices, attr = [], [], []
offset = 0
for vid, geom in six.iteritems(geometry):
if vid in prop:
geom.apply(d)
idl = np.array([tuple(index) for index in list(d.discretization.indexList)])+offset
pts = [(pt.x, pt.y, pt.z) for pt in list(d.discretization.pointList)]
vertices.extend(pts)
offset += len(pts)
indices.extend(idl.tolist())
attr.extend([prop[vid]]*len(pts))
#else:
# attr.extend([0]*len(pts))
mesh = k3d.mesh(vertices=vertices,
indices=indices,
attribute=attr,
color_map=k3d.basic_color_maps.Jet)
return mesh
[docs]
def MTG(g, property_name, plot=None):
"""Return a plot from an MTG object"""
if plot is None:
plot = k3d.plot()
mesh = mtg2mesh(g, property_name)
plot += mesh
plot.lighting = 3
return plot