def test_scene(s): tesselator = pgl.Tesselator() d = s.todict() for pid, pgl_objects in d.iteritems(): for shape in pgl_objects: if not shape.apply(tesselator): print(pid)
def get_height(scene_geometry): """ Calculate the height of objects in the scene Find the coordinates of the points that compose the object and compute the mean of the height coordinates, for each object in the scene. Parameters ---------- scene_geometry: dict([id, geometry]) Dictionnary of geometries of objects in the scene. """ heights = {} tesselator = pgl.Tesselator() for vid, shapes in scene_geometry.items(): S = [] H = [] if not _is_iterable(shapes): shapes = [shapes] for shape in shapes: shape.apply(tesselator) mesh = tesselator.triangulation itri = list(range(mesh.indexListSize())) H += [mesh.faceCenter(i)[2] for i in itri] heights.update({vid: H}) return heights
def as_scene_mesh(pgl_scene): """ Transform a PlantGL scene / PlantGL shape dict to a scene_mesh""" tesselator = pgl.Tesselator() if isinstance(pgl_scene, pgl.Scene): sm = {} def _concat_mesh(mesh1,mesh2): v1, f1 = mesh1 v2, f2 = mesh2 v = numpy.array(v1.tolist() + v2.tolist()) offset = len(v1) f = numpy.array(f1.tolist() + [[i + offset, j + offset, k + offset] for i, j, k in f2.tolist()]) return v, f for pid, pgl_objects in pgl_scene.todict().iteritems(): sm[pid] = reduce(_concat_mesh, [shape_mesh(pgl_object, tesselator) for pgl_object in pgl_objects]) return sm elif isinstance(pgl_scene, dict): return {sh_id: shape_mesh(sh,tesselator) for sh_id, sh in pgl_scene.iteritems()} else: return pgl_scene
def get_source_leaf_and_max_height(g, position='center', relative_height=2. / 3): tesselator = pgl.Tesselator() bbc = pgl.BBoxComputer(tesselator) leaves = get_leaves(g, label='LeafElement') centroids = g.property('centroid') geometries = g.property('geometry') targets = list(leaf for leaf in leaves if leaf in geometries.iterkeys()) for vid in targets: if is_iterable(geometries[vid]): bbc.process(pgl.Scene(geometries[vid])) else: bbc.process(pgl.Scene([pgl.Shape(geometries[vid])])) center = bbc.result.getCenter() centroids[vid] = center zmax = max(centroids.items(), key=lambda x: x[1][2])[1][2] distances = { vid: pgl.norm(centroids[vid] - (0, 0, relative_height * zmax)) for vid in centroids } if position == 'center': return min(distances.items(), key=lambda x: x[1])[0], zmax elif position == 'border': return max(distances.items(), key=lambda x: x[1])[0], zmax
def get_area_and_normal(scene_geometry): def _surf(ind, pts): A, B, C = [pts[i] for i in ind] return pgl.norm(pgl.cross(B - A, C - A)) / 2.0 def _normal(ind, pts): A, B, C = [pts[i] for i in ind] n = pgl.cross(B - A, C - A) return n.normed() tesselator = pgl.Tesselator() areas = {} normals = {} for vid, shapes in scene_geometry.items(): S = [] norm = [] if not _is_iterable(shapes): shapes = [shapes] for shape in shapes: shape.apply(tesselator) mesh = tesselator.triangulation itri = list(range(mesh.indexListSize())) pts = mesh.pointList S += [_surf(mesh.indexAt(i), pts) for i in itri] norm += [_normal(mesh.indexAt(i), pts) for i in itri] areas.update({vid: S}) normals.update({vid: norm}) return areas, normals
def get_leaf_height(self, leaf_geom): from openalea.plantgl import all as pgl tesselator = pgl.Tesselator() bbc = pgl.BBoxComputer(tesselator) if self.is_iterable(leaf_geom): bbc.process(pgl.Scene(leaf_geom)) else: bbc.process(pgl.Scene([pgl.Shape(leaf_geom)])) return bbc.result.getCenter()[2]
def domain3D(domain2D, scene): t = pgl.Tesselator() bbc = pgl.BBoxComputer(t) bbc.process(scene) bbox = bbc.result z_base = bbox.getZMin() z_top = bbox.getZMax() domain3D = (domain2D[0] + (z_base, ), domain2D[1] + (z_top, )) return domain3D
def pgl_to_triangles(pgl_object, tesselator=None): triangles = [] if tesselator is None: tesselator = pgl.Tesselator() pgl_object.apply(tesselator) mesh = tesselator.triangulation if mesh: pts = numpy.array(mesh.pointList, ndmin=2) indices = numpy.array(mesh.indexList, ndmin=2) triangles = [_triangle(itri, pts) for itri in indices] return triangles
def pgl_to_triangles(pgl_object, tesselator=None): triangles = [] if tesselator is None: tesselator = pgl.Tesselator() pgl_object.apply(tesselator) mesh = tesselator.triangulation if mesh: indices = mesh.indexList pts = list(map(tuple, mesh.pointList)) triangles = [(pts[itri[0]], pts[itri[1]], pts[itri[2]]) for itri in indices] return triangles
def surfPerTriangle(sc): """return an array of center, surface cople for every triangle in the scene""" res = [] t = pgl.Tesselator() for sh in sc: sh.geometry.apply(t) d = t.triangulation for i in range(d.indexListSize()): center = (d.pointAt(i,0)+d.pointAt(i,1)+d.pointAt(i,2))/3 surf = pgl.surface (d.pointAt(i,0),d.pointAt(i,1),d.pointAt(i,2)) res += [(center,surf)] return res
def __init__(self, camera = {'type':'perspective', 'distance':1., 'fov':45., 'xc':0., 'yc':0., 'azimuth':0, 'zenith':0.}, image_width = 320, image_height = 280, background = (0,0,0), light_position = (10,0,10), light_color=(1,1,1), working_dir = None): """ Setup a Povray instance :Parameters: - camera: a dict of parameters for positioning the camera - distance: distance from the position of the camera to the look_at point - fov : angle corresponding to the width of the image - xc, yc : coordinates of the center of the scene - azimuth: angle in degree around the vertical axis. az=0. is equialent to have the width of the image align to X direction of the scene - zenith: angle between the view direction and the vertical - image_width: width of the final image in pixel - image_height: height of the final image in pixel - working_dir: A directory for storing povary files. If None, (default), a temporary directory will be created and removed onece the instance is deleted """ try: if working_dir is not None: self.wdir=path(path(working_dir).abspath()) if not self.wdir.exists(): self.wdir.mkdir() self.cleanup_wdir = False else: # build a temporary directory self.wdir = path(tempfile.mkdtemp()) self.cleanup_wdir = True except: raise PovRayError("PovRay can't create its working directory : check for read/write permission or security level") if platform.system() is 'Windows': self.cmdline = 'pvengine +FN +I%s +H%d +W%d -d /exit' else: self.cmdline = 'povray +FN +I%s +H%d +W%d' self.image_width = image_width self.image_height = image_height self.camera = camera self.light_position = light_position self.light_color = light_color self.background = background self.soil = False self.rendered_image_path = None self.tesselator = pgl.Tesselator() #to be removed once cv_camera has been properly integrated self.user_camera = None
def StemElement_mesh(length, diameter_base, diameter_top, classic=False): """ Compute mesh for a stem element - classic indicates """ if classic: solid = True diameter = diameter_base slices = 6 # 6 is the minimal number of slices for a correct computation of star (percentage error lower than 5) stem = pgl.Tapered(diameter_base / 2., diameter_top / 2., pgl.Cylinder(1., length, solid, slices)) tessel = pgl.Tesselator() stem.apply(tessel) mesh = tessel.triangulation else: mesh = slim_cylinder(length, diameter_base / 2., diameter_top / 2.) return mesh
def bbox(pgl_scene, scene_unit='m'): """ Bounding box of a pgl scene""" tesselator = pgl.Tesselator() bbc = pgl.BBoxComputer(tesselator) bbc.process(pgl_scene) box = bbc.result xmin, ymin, zmin = box.getXMin(), box.getYMin(), box.getZMin() xmax, ymax, zmax = box.getXMax(), box.getYMax(), box.getZMax() if scene_unit != 'm': units = {'mm': 0.001, 'cm': 0.01, 'dm': 0.1, 'm': 1, 'dam': 10, 'hm': 100, 'km': 1000} convert = units.get(scene_unit, 1) xmin, ymin, zmin = numpy.array((xmin, ymin, zmin)) * convert xmax, ymax, zmax = numpy.array((xmax, ymax, zmax)) * convert return (xmin, ymin, zmin), (xmax, ymax, zmax)
def get_source_leaf(g, position_source=2./3): tesselator = pgl.Tesselator() bbc = pgl.BBoxComputer(tesselator) leaves = get_leaves(g, label='LeafElement') centroids = g.property('centroid') geometries = g.property('geometry') targets = list(leaf for leaf in leaves if leaf in geometries.iterkeys()) for vid in targets: if isinstance(geometries[vid], collections.Iterable): bbc.process(pgl.Scene(geometries[vid])) else: bbc.process(pgl.Scene([pgl.Shape(geometries[vid])])) center = bbc.result.getCenter() centroids[vid] = center zmax = max(centroids.items(), key=lambda x:x[1][2])[1][2] distances = {vid:pgl.norm(centroids[vid]-(0,0,position_source*zmax)) for vid in centroids} return min(distances.items(), key=lambda x:x[1])[0]
def surfPerLeaf(sc): """return an array of center, surface cople for every leaf in the scene""" res = [] t = pgl.Tesselator() for sh in sc: sh.geometry.apply(t) d = t.triangulation res2=[] lcenter = pgl.Vector3( 0,0,0 ) for i in range(d.indexListSize()): lcenter += (d.pointAt(i,0)+d.pointAt(i,1)+d.pointAt(i,2))/3 surf = pgl.surface (d.pointAt(i,0),d.pointAt(i,1),d.pointAt(i,2)) #where in openalea.plantgl ?? res2 += [surf] lcenter /= len( res2 ) lsurf=sum( res2 ) res+=[ ( lcenter, lsurf ) ] return res
def leaves_in_grid(self, g, label='LeafElement'): geometries = g.property('geometry') centroids = g.property('centroid') tesselator = pgl.Tesselator() bbc = pgl.BBoxComputer(tesselator) leaves = get_leaves(g, label=label) leaves = [l for l in leaves if l in geometries] # Get centroids def centroid(vid): if is_iterable(geometries[vid]): bbc.process(pgl.Scene(geometries[vid])) else: bbc.process(pgl.Scene([pgl.Shape(geometries[vid])])) center = bbc.result.getCenter() centroids[vid] = center if len(leaves) > 0: for vid in leaves: centroid(vid) # Define grid (horizontal layers) zs = [c[2] for c in centroids.itervalues()] minz = min(zs) maxz = max(zs) + self.layer_thickness layers = { l: [] for l in np.arange(minz, maxz, self.layer_thickness) } # Distribute leaves in layers for vid, coords in centroids.iteritems(): z = coords[2] ls = layers.keys() i_layer = np.where( map( lambda x: x <= z < x + self.layer_thickness if z != maxz - self.layer_thickness else x <= z <= x + self.layer_thickness, ls))[0] if len(i_layer) > 0.: layers[ls[i_layer]].append(vid) self.layers = layers else: self.layers = {}
def mtg_to_cscene(g, property_name='geometry'): """Build a caribu-compatible scene from a mtg encoding geometries Args: g: an openalea.mtg.mtg.MTG instance property_name: (str) the name of the property in g where plantGL geometries are encoded Returns: a {primitive_id: [triangles,]} dict.A triangle is a 3-tuple of 3-tuples points coordinates primitive_id is the vertex id. """ geometry = g.property(property_name) tesselator = pgl.Tesselator() cscene = {} for pid in geometry: cscene[pid] = pgl_to_triangles(geometry[pid], tesselator) return cscene
def scene_to_cscene(scene): """ Build a caribu-compatible scene from a PlantGl scene Args: scene: an openalea.plantgl.all.Scene instance Returns: a {primitive_id: [triangles,]} dict.A triangle is a 3-tuple of 3-tuples points coordinates primitive_id is taken as the index of the shape in the scene shape list. """ cscene = {} tesselator = pgl.Tesselator() for pid, pgl_objects in scene.todict().iteritems(): tri_list = sum([ pgl_to_triangles(pgl_object, tesselator) for pgl_object in pgl_objects ], []) if len(tri_list) > 0: cscene[pid] = tri_list return cscene
def test_pgl_scene(): g = potted_syrah() bc = pgl.BBoxComputer(pgl.Tesselator()) s = energy.pgl_scene(g) bc.process(s) bbox = bc.boundingbox zmin, zmax = bbox.getZMin(),bbox.getZMax() assert zmax > zmin > 0 s = energy.pgl_scene(g, flip=True) bc.process(s) bbox = bc.boundingbox zmin, zmax = bbox.getZMin(),bbox.getZMax() assert 0 > zmax > zmin # check that original scene is still z >0 s = energy.pgl_scene(g) bc.process(s) bbox = bc.boundingbox zmin, zmax = bbox.getZMin(), bbox.getZMax() assert zmax > zmin > 0
""" Gather different strategies for modeling dispersal of fungus propagules.
def my_distance(n, sectors): """ Compute the distance with infectable sectors. """ if 'distance' not in n._g._properties: n._g.add_property('distance') tesselator = pgl.Tesselator() bbc = pgl.BBoxComputer(tesselator) def base(geom): if geom: pts = geom.pointList if pts: n = len(pts) assert n % 2 == 0 return 1 / 2 * (pts[0] + pts[n / 2]) def top(geom): if geom: pts = geom.pointList if pts: n = len(pts) assert n % 2 == 0 return 1 / 2 * (pts[n / 2 - 1] + pts[-1]) def bbox_distance(g1, g2): bbc.process(pgl.Scene([g1])) bbox1 = bbc.result bbc.process(pgl.Scene([g2])) bbox2 = bbc.result # TODO bbox.lowerLeftCorner, bbox.upperRightCorner return bbox1.distance(bbox2) def mesh_distance(g1, g2): pts1 = g1.pointList pts2 = g2.pointList if pts1 and pts2: d = maxint for pt in pts2: p1, i = pts1.findClosest(pt) # TODO: To Fix d = pgl.norm(pt - p1) return d else: return maxint def lovel_distance(g1, g2): p1 = base(g1) p2 = top(g2) return pgl.norm(p2 - p1) def opt_distance(n1, n2): g1 = n1.geometry g2 = n2.geometry if not g1 or not g2: return maxint if DISTANCE == 'BOX': return bbox_distance(g1, g2) elif DISTANCE == 'LOVEL': return lovel_distance(g1, g2) elif DISTANCE == 'MESH': return mesh_distance(g1, g2) dists = [opt_distance(n, source) for source in sectors] dist = min(dists) if dists else maxint if not n.distance: n.distance = [] if dist != maxint: n.distance.append((n.age, dist)) print n, dist if dist <= 1: n.color = 0, 0, 255
def __init__(self, database, seed=None): #self.database = database self.database = [] self.tessel = pgl.Tesselator() self.seed = seed
def fit_grid(self, z_adaptive=False): """ Find grid parameters that fit the scene in the RATP grid """ # fit regular grid to scene if self.scene is None: nbx, nby, nbz = self.grid_shape dx, dy, dz = self.grid_resolution # already in meter xo, yo, zo = 0, 0, 0 # origin else: # Find the bounding box that fit the scene tesselator = pgl.Tesselator() bbc = pgl.BBoxComputer(tesselator) bbc.process(self.scene) bbox = bbc.result zsoil = self.z_soil if zsoil is None: zsoil = bbox.getZMin() if self.domain is None: xo = bbox.getXMin() * self.convert # origin yo = bbox.getYMin() * self.convert else: xo = self.domain[0][0] * self.convert yo = self.domain[0][1] * self.convert zo = zsoil * self.convert if self.grid_resolution is not None and self.grid_shape is not None: nbx, nby, nbz = self.grid_shape dx, dy, dz = self.grid_resolution # already in meter else: if self.domain is None: xmax = bbox.getXMax() * self.convert ymax = bbox.getYMax() * self.convert else: xmax = self.domain[1][0] * self.convert ymax = self.domain[1][1] * self.convert zmax = bbox.getZMax() * self.convert if self.grid_resolution is None: nbx, nby, nbz = self.grid_shape if self.domain is None: if nbx > 1: dx = (xmax - xo) / float( nbx - 1 ) # use nbx -1 to ensure min and max are in the grid else: dx = (xmax - xo) * 1.01 if nby > 1: dy = (ymax - yo) / float(nby - 1) else: dy = (ymax - yo) * 1.01 else: dx = (xmax - xo) / float( nbx ) #toric canopies allows coordinate outside the pattern dy = (ymax - yo) / float(nby) if nbz > 1: dz = (zmax - zo) / float(nbz - 1) else: dz = (zmax - zo) * 1.01 # try to accomodate flat scene if dz == 0: dz = (dx + dy) / 2. if dx == 0: dx = (dy + dz) / 2. if dy == 0: dy = (dx + dz) / 2. if self.grid_shape is None: dx, dy, dz = self.grid_resolution if self.domain is None: nbx = int(numpy.ceil((xmax - xo) / float(dx))) nby = int(numpy.ceil((ymax - yo) / float(dy))) else: #dx,dy,dz are adjusted to fit the domain exactly nbx = int((xmax - xo) / float(dx)) nby = int((ymax - yo) / float(dy)) dx = (xmax - xo) / float(nbx) dy = (ymax - yo) / float(nby) nbz = int(numpy.ceil((zmax - zo) / float(dz))) # balance extra-space between both sides of the grid (except z i zsoil has been set) extrax = dx * nbx - (xmax - xo) xo -= (extrax / 2.) extray = dy * nby - (ymax - yo) yo -= (extray / 2.) if self.z_soil is None: extraz = dz * nbz - (zmax - zo) zo -= (extraz / 2.) # dz for all voxels if self.z_resolution is not None: dz = self.z_resolution[::-1] # dz is from top to base for ratp nbz = len(dz) else: dz = [dz] * nbz grid_pars = { 'njx': nbx, 'njy': nby, 'njz': nbz, 'dx': dx, 'dy': dy, 'dz': dz, 'xorig': xo, 'yorig': yo, 'zorig': -zo } # zorig is a z offset in grid.py (grid_z = z + zo) return grid_pars
def shape_mesh(pgl_shape, tesselator=None): if tesselator is None: tesselator = pgl.Tesselator() tesselator.process(pgl_shape) tset = tesselator.result return numpy.array(tset.pointList), numpy.array(tset.indexList)