def _grid_views_default(self): # This needs to generate the geometries and access the materials grid_views = [] cmap = mcm.get_cmap("inferno") for level in range(self.ds.max_level + 1): # We truncate at half of the colormap so that we just get a slight # linear progression color = mcolors.to_hex( cmap(self.cmap_truncate * level / self.ds.max_level)) # Corners is shaped like 8, 3, NGrids this_level = self.ds.index.grid_levels[:, 0] == level corners = np.rollaxis(self.ds.index.grid_corners[:, :, this_level], 2).astype("float32") indices = (((np.arange(corners.shape[0]) * 8)[:, None] + _CORNER_INDICES[None, :]).ravel().astype("uint32")) corners.shape = (corners.size // 3, 3) geometry = pythreejs.BufferGeometry(attributes=dict( position=pythreejs.BufferAttribute(array=corners, normalized=False), index=pythreejs.BufferAttribute(array=indices, normalized=False), )) material = pythreejs.LineBasicMaterial(color=color, linewidth=1, linecap="round", linejoin="round") segments = pythreejs.LineSegments(geometry=geometry, material=material) grid_views.append(segments) return grid_views
def mesh(self, colors={}): import pythreejs as js import numpy as np lines = [] line_colors = [] red = [1, 0, 0] green = [0, 1, 0] exterior = self.project(exterior=True) interior = self.project(exterior=False) for color, polygons in zip([green, red], [exterior, interior]): for polygon in polygons: for segment in polygon.segments(): lines.extend([segment.p1, segment.p2]) line_colors.extend([color, color]) lines = np.array(lines, dtype=np.float32) line_colors = np.array(line_colors, dtype=np.float32) geometry = js.BufferGeometry(attributes={ 'position': js.BufferAttribute(lines, normalized=False), 'color': js.BufferAttribute(line_colors, normalized=False), }, ) material = js.LineBasicMaterial(vertexColors='VertexColors', linewidth=1) return js.LineSegments(geometry, material)
def __add_line_geometry(self, lines, shading, obj=None): lines = lines.astype("float32", copy=False) mi = np.min(lines, axis=0) ma = np.max(lines, axis=0) geometry = p3s.BufferGeometry( attributes={ 'position': p3s.BufferAttribute(lines, normalized=False) }) material = p3s.LineBasicMaterial(linewidth=shading["line_width"], color=shading["line_color"]) #, vertexColors='VertexColors'), lines = p3s.LineSegments(geometry=geometry, material=material) #type='LinePieces') line_obj = { "geometry": geometry, "mesh": lines, "material": material, "max": ma, "min": mi, "type": "Lines", "wireframe": None } if obj: return self.__add_object(line_obj, obj), line_obj else: return self.__add_object(line_obj)
def build_geometry(triangles, positions, normals=None, colors=None): attributes = { "position": three.BufferAttribute( np.array(positions, dtype="float32").reshape(-1, 3), normalized=False, ), "index": three.BufferAttribute( np.array(triangles, dtype="uint32"), normalized=False, ), } if normals: attributes["normal"] = three.BufferAttribute( np.array(normals, dtype="float32").reshape(-1, 3), normalized=False, ) if colors: attributes["color"] = three.BufferAttribute( np.array(colors, dtype="uint8").reshape(-1, 4), normalized=False, ) geometry = three.BufferGeometry(attributes=attributes) # Compute normals from face geometry if they were not specified. if not normals: geometry.exec_three_obj_method("computeVertexNormals") return geometry
def _make_threejs_primitive(self): geometry = pythreejs.BufferGeometry() material = pythreejs.MeshStandardMaterial( vertexColors=pythreejs.enums.Colors.VertexColors, metalness=0.05, roughness=.9) result = pythreejs.Mesh(geometry, material) return result
def traverse_func(node, trans): # Underlying mesh mesh = node.primitive.mesh if mesh is None: return # Iterate through all triangles vs = [] def process_triangle(face_index, tri): vs.append(list(tri.p1.p)) vs.append(list(tri.p2.p)) vs.append(list(tri.p3.p)) mesh.foreach_triangle(process_triangle) # Create geometry ps_attr = three.BufferAttribute(array=vs, normalized=False) geom = three.BufferGeometry(attributes={'position': ps_attr}) # Create mesh mesh = three.Mesh(geometry=geom, material=mat_default) mesh.matrixAutoUpdate = False mesh.matrix = trans.T.flatten().tolist() scene.add(mesh)
def __get_points_from_pointcloud(self): vertices = self.geometry.vertices.astype(np.float32) buffer = np.empty((int(vertices.shape[0] * 3), 3), dtype=np.float32).reshape(-1, 3) buffer[:vertices.shape[0]] = vertices vertex_buffer = self.__as_buffer_attr(buffer) points = three.BufferGeometry(attributes={'position': vertex_buffer}) points.exec_three_obj_method("setDrawRange", 0, vertices.shape[0]) return points
def scatter_children(points, color="red", size=4): scene_children = [] geometry_point = p3js.BufferGeometry( attributes={"position": p3js.BufferAttribute(array=points)}) material_point = p3js.PointsMaterial(color=color, size=size) pts = p3js.Points(geometry_point, material_point) scene_children.append(pts) return scene_children
def get_pointcloud_pythreejs(xyz, colors): points_geometry = pythreejs.BufferGeometry(attributes=dict( position=pythreejs.BufferAttribute(xyz, normalized=False), color=pythreejs.BufferAttribute(list(map(tuple, colors))))) points_material = pythreejs.PointsMaterial(vertexColors='VertexColors') return pythreejs.Points(geometry=points_geometry, material=points_material)
def __get_wireframe_from_boundary(self): edges = self.geometry.vertices[self.geometry.as_edges_flat()].astype( np.float32) buffer = np.empty((int(edges.shape[0] * 3), 3), dtype=np.float32).reshape(-1, 3) buffer[:edges.shape[0]] = edges vertices = self.__as_buffer_attr(buffer) wireframe = three.BufferGeometry(attributes={'position': vertices}) wireframe.exec_three_obj_method("setDrawRange", 0, edges.shape[0]) return wireframe
def add_points(self, points, c=None, shading={}, obj=None, **kwargs): shading.update(kwargs) if len(points.shape) == 1: if len(points) == 2: points = np.array([[points[0], points[1], 0]]) else: if points.shape[1] == 2: points = np.append(points, np.zeros([points.shape[0], 1]), 1) sh = self.__get_shading(shading) points = points.astype("float32", copy=False) mi = np.min(points, axis=0) ma = np.max(points, axis=0) g_attributes = { "position": p3s.BufferAttribute(points, normalized=False) } m_attributes = {"size": sh["point_size"]} if sh["point_shape"] == "circle": # Plot circles tex = p3s.DataTexture(data=gen_circle(16, 16), format="RGBAFormat", type="FloatType") m_attributes["map"] = tex m_attributes["alphaTest"] = 0.5 m_attributes["transparency"] = True else: # Plot squares pass colors, v_colors = self.__get_point_colors(points, c, sh) if v_colors: # Colors per point m_attributes["vertexColors"] = 'VertexColors' g_attributes["color"] = p3s.BufferAttribute(colors, normalized=False) else: # Colors for all points m_attributes["color"] = colors material = p3s.PointsMaterial(**m_attributes) geometry = p3s.BufferGeometry(attributes=g_attributes) points = p3s.Points(geometry=geometry, material=material) point_obj = { "geometry": geometry, "mesh": points, "material": material, "max": ma, "min": mi, "type": "Points", "wireframe": None } if obj: return self.__add_object(point_obj, obj), point_obj else: return self.__add_object(point_obj)
def create_points_geometry(self): """ Make a PointsGeometry using pythreejs """ points_geometry = p3.BufferGeometry( attributes={ 'position': p3.BufferAttribute(array=self.positions), 'rgba_color': p3.BufferAttribute(array=self.slice_data(None)) }) points_material = self.create_points_material() points = p3.Points(geometry=points_geometry, material=points_material) return points_geometry, points_material, points
def __get_wireframe_from_boundary(self): #edges in the boundary box edges = self.geometry.vertices[self.geometry.as_edges_flat()].astype(np.float32) #The function empty returns an array without values initialized buffer = np.empty((int(edges.shape[0] * 3), 3), dtype=np.float32).reshape(-1, 3) buffer[:edges.shape[0]] = edges vertices = self.__as_buffer_attr(buffer) wireframe = three.BufferGeometry(attributes={'position': vertices}) #Excute the method specified by `method_name` on the three object, with arguments `args` #SetDrawRange is a function that sets the attribute DrawRange which determines the part of the geometry to render. (start, end) wireframe.exec_three_obj_method("setDrawRange", 0, edges.shape[0]) return wireframe
def _create_point_cloud(self, pos_array): """ Make a PointsGeometry using pythreejs """ rgba_shape = list(pos_array.shape) rgba_shape[1] += 1 self.points_geometry = p3.BufferGeometry( attributes={ 'position': p3.BufferAttribute(array=pos_array), 'rgba_color': p3.BufferAttribute(array=np.ones(rgba_shape, dtype=np.float32)) }) self.point_cloud = p3.Points(geometry=self.points_geometry, material=self.points_material)
def geometry(self): attributes = dict( position=pythreejs.BufferAttribute(self.vertices, normalized=False), index=pythreejs.BufferAttribute( self.indices.ravel(order="C").astype("u4"), normalized=False ), ) if self.attributes is not None: attributes["color"] = pythreejs.BufferAttribute(self.attributes) # Face colors requires # https://speakerdeck.com/yomotsu/low-level-apis-using-three-dot-js?slide=22 # and # https://github.com/mrdoob/three.js/blob/master/src/renderers/shaders/ShaderLib.js geometry = pythreejs.BufferGeometry(attributes=attributes) geometry.exec_three_obj_method("computeFaceNormals") return geometry
def mesh(self): import numpy as np import pythreejs as js wireframe = self.view_data.get('wireframe', False) def triangles(polygon): points = polygon.points return [(points[0], points[ix], points[ix + 1]) for ix in range(1, len(polygon.points) - 1)] def _ba(vs): points = np.array(vs, dtype=np.float32) return js.BufferAttribute(array=points, normalized=False) vertices = [ list(p.xyz) for polygon in self.polygons for t in triangles(polygon) for p in t ] normals = [ list(polygon.plane.normal.xyz) for polygon in self.polygons for t in triangles(polygon) for p in t ] geometry = js.BufferGeometry( attributes={ 'position': _ba(vertices), 'normal': _ba(normals) }, ) if not wireframe: color = self.view_data.get('color', 'white') material = material=js.MeshLambertMaterial(color=color) opacity = self.view_data.get('opacity') if opacity is not None: material.opacity = opacity material.transparent = True return js.Mesh(geometry, material) else: color = self.view_data.get('color', '#00ff00') material = js.MeshBasicMaterial(color=color, wireframe=True) return js.Mesh(geometry, material)
def _get_mesh(self, u): if self.codim == 2: u = u[self.entity_map] elif self.grid.reference_element == triangle: u = np.repeat(u, 3) else: u = np.tile(np.repeat(u, 3), 2) data = p3js.BufferAttribute(_normalize(u, self.vmin, self.vmax), normalized=True) geo = p3js.BufferGeometry( index=self.buffer_faces, attributes=dict(position=self.buffer_vertices, data=data)) mesh = p3js.Mesh(geometry=geo, material=self.material) mesh.visible = False # translate to origin where the camera is looking by default, avoids camera not updating in nbconvert run mesh.position = tuple(p - i for p, i in zip(mesh.position, self.mesh_center)) return mesh
def display_portal(th_scene, portal): """Display portal.""" def portal_vertices(portal): p1 = np.array(portal[0]) p2 = np.array(portal[1]) p4 = np.array(portal[2]) p3 = p1 + (p2 - p1) + (p4 - p1) return [p1, p2, p3, p1, p3, p4] ps_attr = three.BufferAttribute(array=portal_vertices(portal), normalized=False) geom = three.BufferGeometry(attributes={'position': ps_attr}) mat = three.MeshBasicMaterial(color='#ff0000', transparent=True, opacity=0.1, side='DoubleSide') mesh = three.Mesh(geometry=geom, material=mat) th_scene.add(mesh)
def __get_drawable_from_boundary(self): geometry_attributes = {} tris, vtx_normals = self.geometry._as_threejs_triangle_soup() new_colors = self.geometry_color[ self.geometry._as_threejs_colors()].astype(np.float32) interleaved_array = np.concatenate((tris, new_colors, vtx_normals), axis=1) buffer = three.InterleavedBuffer(array=interleaved_array, stride=3) geometry_attributes['position'] = three.InterleavedBufferAttribute( data=buffer, itemSize=3, dynamic=True) geometry_attributes['color'] = three.InterleavedBufferAttribute( data=buffer, itemSize=3, offset=3, dynamic=True) geometry_attributes['normal'] = three.InterleavedBufferAttribute( data=buffer, itemSize=3, offset=6, dynamic=True) drawable_geometry = three.BufferGeometry( attributes=geometry_attributes) return drawable_geometry
def axes_1d(x, y, color="#000000", linewidth=1.5): N = len(x) pts = np.zeros([5, 3]) xmin = np.amin(x) xmax = np.amax(x) ymin = np.amin(y) ymax = np.amax(y) pts[:, 0] = x pts[:, 1] = y geometry = p3.BufferGeometry(attributes={ 'position': p3.BufferAttribute(array=pts), }) material = p3.LineBasicMaterial(color=color, linewidth=linewidth) line = p3.Line(geometry=geometry, material=material) width = 800 height = 500
def to_edge_mesh(surf, mapper, prop, use_edge_coloring=True, use_lines=False): """Convert a pyvista surface to a three.js edge mesh.""" # extract all edges from the surface. Should not use triangular # mesh here as mesh may contain more than triangular faces if use_lines: edges_mesh = surf edges = segment_poly_cells(surf) else: edges_mesh = surf.extract_all_edges() edges = edges_mesh.lines.reshape(-1, 3)[:, 1:] attr = { 'position': array_to_float_buffer(edges_mesh.points), 'index': cast_to_min_size(edges, surf.n_points), } # add in colors coloring = get_coloring(mapper, surf) if coloring != 'NoColors' and not use_edge_coloring: if mapper.GetScalarModeAsString() == 'UsePointData': edge_scalars = edges_mesh.point_data.active_scalars edge_colors = map_scalars(mapper, edge_scalars) attr['color'] = array_to_float_buffer(edge_colors) edge_geo = tjs.BufferGeometry(attributes=attr) mesh_attr = {} if coloring != 'NoColors': mesh_attr['vertexColors'] = coloring if use_edge_coloring: edge_color = prop.GetEdgeColor() else: edge_color = prop.GetColor() edge_mat = tjs.LineBasicMaterial(color=color_to_hex(edge_color), linewidth=prop.GetLineWidth(), opacity=prop.GetOpacity(), side='FrontSide', **mesh_attr) return tjs.LineSegments(edge_geo, edge_mat)
def to_tjs_points(dataset, mapper, prop): """Extract the points from a dataset and return a buffered geometry.""" attr = { 'position': array_to_float_buffer(dataset.points), } coloring = get_coloring(mapper, dataset) if coloring == 'VertexColors': colors = map_scalars(mapper, dataset.point_data.active_scalars) attr['color'] = array_to_float_buffer(colors) geo = tjs.BufferGeometry(attributes=attr) m_attr = { 'color': color_to_hex(prop.GetColor()), 'size': prop.GetPointSize() / 100, 'vertexColors': coloring, } point_mat = tjs.PointsMaterial(**m_attr) return tjs.Points(geo, point_mat)
def plot_1d(x, y, color="blue", linewidth=2, background="#DDDDDD"): if len(x) != len(y): raise RuntimeError("bad shape") N = len(x) dx = config.figure["width"] dy = config.figure["height"] xmin = np.amin(x) ymin = np.amin(y) scale_x = dx / (np.amax(x) - xmin) scale_y = dy / (np.amax(y) - ymin) pts = np.zeros([N, 3], dtype=np.float32) pts[:, 0] = (x - xmin) * scale_x pts[:, 1] = (y - ymin) * scale_y # arr = p3.BufferAttribute(array=pts) geometry = p3.BufferGeometry(attributes={ 'position': p3.BufferAttribute(array=pts), }) material = p3.LineBasicMaterial(color=color, linewidth=linewidth) line = p3.Line(geometry=geometry, material=material) # width = 800 # height= 500 # Create the threejs scene with ambient light and camera # camera = p3.PerspectiveCamera(position=[0.5*dx, 0.5*dy, 0], # aspect=dx / dy) camera = p3.OrthographicCamera(0, dx, dy, 0, 0.5 * dx, -0.5 * dx) return render(objects=line, camera=camera, background=background, enableRotate=False, width=dx, height=dy)
def visualize(self, colors={}): from ..space import Vector import pythreejs as js import numpy as np prior = Motion(x=0, y=0, z=0, f=0) lines = [] line_colors = [] for parent, command in self.commands(): if isinstance(command, Motion): updated = prior.merge(command) pv = Vector(*prior.xyz) uv = Vector(*updated.xyz) delta = pv - uv start = (uv + pv) / 2 lines.extend([prior.xyz, updated.xyz]) a1 = delta.rotate(Vector(0, 0, 1), tau / 16) * 0.1 a2 = delta.rotate(Vector(0, 0, 1), -tau / 16) * 0.1 lines.extend( [start, (a1 + start).xyz, start, (a2 + start).xyz]) prior = updated color = getattr(parent, 'color', [0, 1, 0]) line_colors.extend([color, color]) line_colors.extend([color, color]) line_colors.extend([color, color]) lines = np.array(lines, dtype=np.float32) line_colors = np.array(line_colors, dtype=np.float32) geometry = js.BufferGeometry(attributes={ 'position': js.BufferAttribute(lines, normalized=False), 'color': js.BufferAttribute(line_colors, normalized=False), }, ) material = js.LineBasicMaterial(vertexColors='VertexColors', linewidth=1) return js.LineSegments(geometry, material)
def _render_obj(self, rendered_obj, **kw): obj_geometry = pjs.BufferGeometry(attributes=dict( position=pjs.BufferAttribute(rendered_obj.plot_verts), color=pjs.BufferAttribute(rendered_obj.base_cols), normal=pjs.BufferAttribute( rendered_obj.face_normals.astype('float32')))) vertices = rendered_obj.vertices # Create a mesh. Note that the material need to be told to use the vertex colors. my_object_mesh = pjs.Mesh( geometry=obj_geometry, material=pjs.MeshLambertMaterial(vertexColors='VertexColors'), position=[0, 0, 0], ) line_material = pjs.LineBasicMaterial(color='#ffffff', transparent=True, opacity=0.3, linewidth=1.0) my_object_wireframe_mesh = pjs.LineSegments( geometry=obj_geometry, material=line_material, position=[0, 0, 0], ) n_vert = vertices.shape[0] center = vertices.mean(axis=0) extents = self._get_extents(vertices) max_delta = np.max(extents[:, 1] - extents[:, 0]) camPos = [center[i] + 4 * max_delta for i in range(3)] light_pos = [center[i] + (i + 3) * max_delta for i in range(3)] # Set up a scene and render it: camera = pjs.PerspectiveCamera(position=camPos, fov=20, children=[ pjs.DirectionalLight( color='#ffffff', position=light_pos, intensity=0.5) ]) camera.up = (0, 0, 1) v = [0.0, 0.0, 0.0] if n_vert > 0: v = vertices[0].tolist() select_point_geom = pjs.SphereGeometry(radius=1.0) select_point_mesh = pjs.Mesh( select_point_geom, material=pjs.MeshBasicMaterial(color=SELECTED_VERTEX_COLOR), position=v, scale=(0.0, 0.0, 0.0)) #select_edge_mesh = pjs.ArrowHelper(dir=pjs.Vector3(1.0, 0.0, 0.0), origin=pjs.Vector3(0.0, 0.0, 0.0), length=1.0, # hex=SELECTED_EDGE_COLOR_INT, headLength=0.1, headWidth=0.05) arrow_cyl_mesh = pjs.Mesh(geometry=pjs.SphereGeometry(radius=0.01), material=pjs.MeshLambertMaterial()) arrow_head_mesh = pjs.Mesh(geometry=pjs.SphereGeometry(radius=0.001), material=pjs.MeshLambertMaterial()) scene_things = [ my_object_mesh, my_object_wireframe_mesh, select_point_mesh, arrow_cyl_mesh, arrow_head_mesh, camera, pjs.AmbientLight(color='#888888') ] if self.draw_grids: grids, space = self._get_grids(vertices) scene_things.append(grids) scene = pjs.Scene(children=scene_things, background=BACKGROUND_COLOR) click_picker = pjs.Picker(controlling=my_object_mesh, event='dblclick') out = Output() top_msg = HTML() def on_dblclick(change): if change['name'] == 'point': try: point = np.array(change['new']) face = click_picker.faceIndex face_points = rendered_obj.face_verts[face] face_vecs = face_points - np.roll(face_points, 1, axis=0) edge_lens = np.sqrt((face_vecs**2).sum(axis=1)) point_vecs = face_points - point[np.newaxis, :] point_dists = (point_vecs**2).sum(axis=1) min_point = np.argmin(point_dists) v1s = point_vecs.copy() v2s = np.roll(v1s, -1, axis=0) edge_mids = 0.5 * (v2s + v1s) edge_mid_dists = (edge_mids**2).sum(axis=1) min_edge_point = np.argmin(edge_mid_dists) edge_start = min_edge_point edge = face * 3 + edge_start close_vert = rendered_obj.face_verts[face, min_point] edge_start_vert = rendered_obj.face_verts[face, edge_start] edge_end_vert = rendered_obj.face_verts[face, (edge_start + 1) % 3] vertex = face * 3 + min_point radius = min( [edge_lens.max() * 0.02, 0.1 * edge_lens.min()]) edge_head_length = radius * 4 edge_head_width = radius * 2 select_point_mesh.scale = (radius, radius, radius) top_msg.value = '<font color="{}">selected face: {}</font>, <font color="{}">edge: {}</font>, <font color="{}"> vertex: {}</font>'.format( SELECTED_FACE_COLOR, face, SELECTED_EDGE_COLOR, edge, SELECTED_VERTEX_COLOR, vertex) newcols = rendered_obj.base_cols.copy() newcols[face * 3:(face + 1) * 3] = np.array( SELECTED_FACE_RGB, dtype='float32') select_point_mesh.position = close_vert.tolist() obj_geometry.attributes['color'].array = newcols with out: make_arrow(arrow_cyl_mesh, arrow_head_mesh, edge_start_vert, edge_end_vert, radius / 2, radius, radius * 3, SELECTED_EDGE_COLOR) except: with out: print(traceback.format_exc()) click_picker.observe(on_dblclick, names=['point']) renderer_obj = pjs.Renderer( camera=camera, background='#cccc88', background_opacity=0, scene=scene, controls=[pjs.OrbitControls(controlling=camera), click_picker], width=self.width, height=self.height) display_things = [top_msg, renderer_obj, out] if self.draw_grids: s = """ <svg width="{}" height="30"> <rect width="20" height="20" x="{}" y="0" style="fill:none;stroke-width:1;stroke:rgb(0,255,0)" /> <text x="{}" y="15">={:.1f}</text> Sorry, your browser does not support inline SVG. </svg>""".format(self.width, self.width // 2, self.width // 2 + 25, space) display_things.append(HTML(s)) display(VBox(display_things))
def add_mesh(self, v, f, c=None, uv=None, shading={}, texture_data=None): sh = self.__get_shading(shading) mesh_obj = {} #it is a tet if v.shape[1] == 3 and f.shape[1] == 4: f_tmp = np.ndarray([f.shape[0]*4, 3], dtype=f.dtype) for i in range(f.shape[0]): f_tmp[i*4+0] = np.array([f[i][1], f[i][0], f[i][2]]) f_tmp[i*4+1] = np.array([f[i][0], f[i][1], f[i][3]]) f_tmp[i*4+2] = np.array([f[i][1], f[i][2], f[i][3]]) f_tmp[i*4+3] = np.array([f[i][2], f[i][0], f[i][3]]) f = f_tmp if v.shape[1] == 2: v = np.append(v, np.zeros([v.shape[0], 1]), 1) # Type adjustment vertices v = v.astype("float32", copy=False) # Color setup colors, coloring = self.__get_colors(v, f, c, sh) # Type adjustment faces and colors c = colors.astype("float32", copy=False) # Material and geometry setup ba_dict = {"color": p3s.BufferAttribute(c)} if coloring == "FaceColors": verts = np.zeros((f.shape[0]*3, 3), dtype="float32") for ii in range(f.shape[0]): #print(ii*3, f[ii]) verts[ii*3] = v[f[ii,0]] verts[ii*3+1] = v[f[ii,1]] verts[ii*3+2] = v[f[ii,2]] v = verts else: f = f.astype("uint32", copy=False).ravel() ba_dict["index"] = p3s.BufferAttribute(f, normalized=False) ba_dict["position"] = p3s.BufferAttribute(v, normalized=False) if type(uv) != type(None): uv = (uv - np.min(uv)) / (np.max(uv) - np.min(uv)) if texture_data is None: texture_data = gen_checkers(20, 20) tex = p3s.DataTexture(data=texture_data, format="RGBFormat", type="FloatType") material = p3s.MeshStandardMaterial(map=tex, reflectivity=sh["reflectivity"], side=sh["side"], roughness=sh["roughness"], metalness=sh["metalness"], flatShading=sh["flat"], polygonOffset=True, polygonOffsetFactor= 1, polygonOffsetUnits=5) ba_dict["uv"] = p3s.BufferAttribute(uv.astype("float32", copy=False)) else: material = p3s.MeshStandardMaterial(vertexColors=coloring, reflectivity=sh["reflectivity"], side=sh["side"], roughness=sh["roughness"], metalness=sh["metalness"], flatShading=sh["flat"], polygonOffset=True, polygonOffsetFactor= 1, polygonOffsetUnits=5) geometry = p3s.BufferGeometry(attributes=ba_dict) if coloring == "VertexColors": geometry.exec_three_obj_method('computeVertexNormals') else: geometry.exec_three_obj_method('computeFaceNormals') # Mesh setup mesh = p3s.Mesh(geometry=geometry, material=material) # Wireframe setup mesh_obj["wireframe"] = None if sh["wireframe"]: wf_geometry = p3s.WireframeGeometry(mesh.geometry) # WireframeGeometry wf_material = p3s.LineBasicMaterial(color=sh["wire_color"], linewidth=sh["wire_width"]) wireframe = p3s.LineSegments(wf_geometry, wf_material) mesh.add(wireframe) mesh_obj["wireframe"] = wireframe # Bounding box setup if sh["bbox"]: v_box, f_box = self.__get_bbox(v) _, bbox = self.add_edges(v_box, f_box, sh, mesh) mesh_obj["bbox"] = [bbox, v_box, f_box] # Object setup mesh_obj["max"] = np.max(v, axis=0) mesh_obj["min"] = np.min(v, axis=0) mesh_obj["geometry"] = geometry mesh_obj["mesh"] = mesh mesh_obj["material"] = material mesh_obj["type"] = "Mesh" mesh_obj["shading"] = sh mesh_obj["coloring"] = coloring mesh_obj["arrays"] = [v, f, c] # TODO replays with proper storage or remove if not needed return self.__add_object(mesh_obj)
def __get_drawable_from_boundary(self): geometry_attributes = {} tris, vtx_normals = self.geometry._as_threejs_triangle_soup() new_colors = self.geometry_color[self.geometry._as_threejs_colors()].astype(np.float32) if self.geometry.uvcoords is not None: uvcoords = self.geometry.uvcoords.astype(np.float32) coor = self.geometry.coor uv = [] if len(uvcoords) != 0: #corrispondenza delle coordinate uv in triangle soup #Uv coords in triangle soup if 'Quadmesh' in self.type: coor = np.c_[coor[:,:3], coor[:,2:], coor[:,0]] coor = coor.flatten() for c in coor: uv.append(uvcoords[c - 1]) self.faceVertexUvs = np.array(uv).astype(np.float32) if len(self.faceVertexUvs) != 0 : interleaved_array = np.concatenate((tris, new_colors, vtx_normals, self.faceVertexUvs), axis=1) buffer = three.InterleavedBuffer(array=interleaved_array, stride=4) else: interleaved_array = np.concatenate((tris, new_colors, vtx_normals), axis=1) buffer = three.InterleavedBuffer(array=interleaved_array, stride=3) #Making the interleavedBuffer using the interleaved_array made up of the triangle soup, the color and the vertices' normals, with a stride of 3 #itemsize = item size, dynamic = (is the normalized attribute o f the super class)?, offset = it's the offset from the start item, geometry_attributes['position'] = three.InterleavedBufferAttribute(data=buffer, itemSize=3, dynamic = True) geometry_attributes['color'] = three.InterleavedBufferAttribute(data=buffer, itemSize=3, offset=3, dynamic=True) geometry_attributes['normal'] = three.InterleavedBufferAttribute(data=buffer, itemSize=3, offset=6, dynamic=True) if self.geometry.material is not {} or self.geometry.texture is not None : geometry_attributes['uv'] = three.InterleavedBufferAttribute(data=buffer, itemSize=2, offset=9, dynamic=True) # # Buffer Geometry = an efficient representation of mesh, line, or point geometry # Includes vertex positions, face indices, normals, colors, UVs, and custom attributes within buffers drawable_geometry = three.BufferGeometry(attributes=geometry_attributes) #The multiplier is used because groups need faces' indices in triangle soup and 'count' counts only the number of faces per material mult = 1 if 'Trimesh' in self.type: mult = 3 elif 'Quadmesh' in self.type: mult = 6 if len(self.geometry.material) != 0: ''' group = { start: Integer, count: Integer, materialIndex: Integer } where: - start : the first triangle index of the group - count : how many indices are included - materialIndex : the material array index to use for this group ''' i = 0 for g in self.geometry.groups: if i == 0: n = copy.copy(g) drawable_geometry.exec_three_obj_method("addGroup", 0, mult * self.geometry.groups[g], self.search_key(g)) else: drawable_geometry.exec_three_obj_method("addGroup", mult * self.geometry.groups[n], mult * self.geometry.groups[g], self.search_key(g)) n = copy.copy(g) i = i + 1 return drawable_geometry
def setGeometry(self, vertices, idxs, normals, preserveExisting=False, updateModelMatrix=False, textureMap=None, scalarField=None, vectorField=None): self.scalarField = scalarField self.vectorField = vectorField if (updateModelMatrix): translate = -np.mean(vertices, axis=0) self.bbSize = np.max(np.abs(vertices + translate)) scaleFactor = 2.0 / self.bbSize self.objects.scale = [scaleFactor, scaleFactor, scaleFactor] self.objects.position = tuple(scaleFactor * translate) ######################################################################## # Construct the raw attributes describing the new mesh. ######################################################################## attrRaw = {'position': vertices, 'index': idxs.ravel(), 'normal': normals} if (textureMap is not None): attrRaw['uv'] = np.array(textureMap.uv, dtype=np.float32) useVertexColors = False if (self.scalarField is not None): # Construct scalar field from raw data array if necessary if (not isinstance(self.scalarField, ScalarField)): self.scalarField = ScalarField(self.mesh, self.scalarField) self.scalarField.validateSize(vertices.shape[0], idxs.shape[0]) attrRaw['color'] = np.array(self.scalarField.colors(), dtype=np.float32) if (self.scalarField.domainType == DomainType.PER_TRI): # Replicate vertex data in the per-face case (positions, normal, uv) and remove index buffer; replicate colors x3 # This is needed according to https://stackoverflow.com/questions/41670308/three-buffergeometry-how-do-i-manually-set-face-colors # since apparently indexed geometry doesn't support the 'FaceColors' option. replicateAttributesPerTriCorner(attrRaw) useVertexColors = True # Turn the current mesh into a ghost if preserveExisting if (preserveExisting and (self.currMesh is not None)): oldMesh = self.currMesh self.currMesh = None oldMesh.material = self.materialLibrary.ghostMaterial(oldMesh.material) self.meshes.remove(oldMesh) self.ghostMeshes.add(oldMesh) # Also convert the current vector field into a ghost (if one is displayed) if (self.vectorFieldMesh in self.meshes.children): oldVFMesh = self.vectorFieldMesh self.vectorFieldMesh = None oldVFMesh.material.transparent = True colors = oldVFMesh.geometry.attributes['arrowColor'].array colors[:, 3] = 0.25 oldVFMesh.geometry.attributes['arrowColor'].array = colors self.meshes.remove(oldVFMesh) self.ghostMeshes.add(oldVFMesh) else: self.__cleanMeshes(self.ghostMeshes) material = self.materialLibrary.material(useVertexColors, None if textureMap is None else textureMap.dataTex) ######################################################################## # Build or update mesh from the raw attributes. ######################################################################## stashableKeys = ['index', 'color', 'uv'] def allocateUpdateOrStashBufferAttribute(attr, key): # Verify invariant that attributes, if they exist, must either be # attached to the current geometry or in the stash (but not both) assert((key not in attr) or (key not in self.bufferAttributeStash)) if key in attrRaw: if key in self.bufferAttributeStash: # Reuse the stashed index buffer attr[key] = self.bufferAttributeStash[key] self.bufferAttributeStash.pop(key) # Update existing attribute or allocate it for the first time if key in attr: attr[key].array = attrRaw[key] else: attr[key] = pythreejs.BufferAttribute(attrRaw[key]) else: if key in attr: # Stash the existing, unneeded attribute self.bufferAttributeStash[key] = attr[key] attr.pop(key) # Avoid flicker/partial redraws during updates if self.avoidRedrawFlicker: self.renderer.pauseRendering() if (self.currMesh is None): attr = {} presentKeys = list(attrRaw.keys()) for key in presentKeys: if key in stashableKeys: allocateUpdateOrStashBufferAttribute(attr, key) attrRaw.pop(key) attr.update({k: pythreejs.BufferAttribute(v) for k, v in attrRaw.items()}) geom = pythreejs.BufferGeometry(attributes=attr) m = self.MeshConstructor(geometry=geom, material=material) self.currMesh = m self.meshes.add(m) else: # Update the current mesh... attr = self.currMesh.geometry.attributes.copy() attr['position'].array = attrRaw['position'] attr['normal' ].array = attrRaw['normal'] for key in stashableKeys: allocateUpdateOrStashBufferAttribute(attr, key) self.currMesh.geometry.attributes = attr self.currMesh.material = material # If we reallocated the current mesh (preserveExisting), we need to point # the wireframe/points mesh at the new geometry. if self.wireframeMesh is not None: self.wireframeMesh.geometry = self.currMesh.geometry if self.pointsMesh is not None: self.pointsMesh.geometry = self.currMesh.geometry ######################################################################## # Build/update the vector field mesh if requested (otherwise hide it). ######################################################################## if (self.vectorField is not None): # Construct vector field from raw data array if necessary if (not isinstance(self.vectorField, VectorField)): self.vectorField = VectorField(self.mesh, self.vectorField) self.vectorField.validateSize(vertices.shape[0], idxs.shape[0]) self.vectorFieldMesh = self.vectorField.getArrows(vertices, idxs, material=self.arrowMaterial, existingMesh=self.vectorFieldMesh) self.arrowMaterial = self.vectorFieldMesh.material self.arrowMaterial.updateUniforms(arrowSizePx_x = self.arrowSize, rendererWidth = self.renderer.width, targetDepth = np.linalg.norm(np.array(self.cam.position) - np.array(self.controls.target)), arrowAlignment = self.vectorField.align.getRelativeOffset()) self.controls.shaderMaterial = self.arrowMaterial if (self.vectorFieldMesh not in self.meshes.children): self.meshes.add(self.vectorFieldMesh) else: if (self.vectorFieldMesh in self.meshes.children): self.meshes.remove(self.vectorFieldMesh) if self.avoidRedrawFlicker: # The scene is now complete; reenable rendering and redraw immediatley. self.renderer.resumeRendering()
def mesh_animation(times, xt, faces): """ Animate a mesh from a sequence of mesh vertex positions Args: times - a list of time values t_i at which the configuration x is specified xt - i.e., x(t). A list of arrays representing mesh vertex positions at times t_i. Dimensions of each array should be the same as that of mesh.geometry.array TODO nt - n(t) vertex normals faces - array of faces, with vertex loop for each face Side effects: displays rendering of mesh, with animation action Returns: None TODO renderer - THREE.Render to show the default scene TODO position_action - THREE.AnimationAction IPython widget """ position_morph_attrs = [] for pos in xt[ 1:]: # xt[0] uses as the Mesh's default/initial vertex position position_morph_attrs.append( THREE.BufferAttribute(pos, normalized=False)) # Testing mesh.geometry.morphAttributes = {'position': position_morph_attrs} geom = THREE.BufferGeometry( attributes={ 'position': THREE.BufferAttribute(xt[0], normalized=False), 'index': THREE.BufferAttribute(faces.ravel()) }, morphAttributes={'position': position_morph_attrs}) matl = THREE.MeshStandardMaterial(side='DoubleSide', color='red', wireframe=True, morphTargets=True) mesh = THREE.Mesh(geom, matl) # create key frames position_track = THREE.NumberKeyframeTrack( name='.morphTargetInfluences[0]', times=times, values=list(range(0, len(times)))) # create animation clip from the morph targets position_clip = THREE.AnimationClip(tracks=[position_track]) # create animation action position_action = THREE.AnimationAction(THREE.AnimationMixer(mesh), position_clip, mesh) # TESTING camera = THREE.PerspectiveCamera(position=[2, 1, 2], aspect=600 / 400) scene = THREE.Scene(children=[ mesh, camera, THREE.AxesHelper(0.2), THREE.DirectionalLight(position=[3, 5, 1], intensity=0.6), THREE.AmbientLight(intensity=0.5) ]) renderer = THREE.Renderer( camera=camera, scene=scene, controls=[THREE.OrbitControls(controlling=camera)], width=600, height=400) display(renderer, position_action)
def plot(self, backend="pythreejs", width=800, height=500, background="black", mesh=False, use_as_color=["red", "green", "blue"], cmap="hsv", return_scene=False, output_name="pyntcloud_plot", polylines={}): """Visualize a PyntCloud using different backends. Parameters ---------- backend: {"pythreejs", "threejs"}, optional Default: "pythreejs" Used to select one of the available libraries for plotting. width: int, optional Default: 800 height: int, optional Default: 500 background: str, optional Default: "black" Used to select the default color of the background. In some backends, i.e "pythreejs" the background can be dynamically changed. use_as_color: str or ["red", "green", "blue"], optional Default: ["red", "green", "blue"] Indicates which scalar fields will be used to colorize the rendered point cloud. cmap: str, optional Default: "hsv" Color map that will be used to convert a single scalar field into rgb. Check matplotlib cmaps. return_scene: bool, optional Default: False. Used with "pythreejs" backend in order to return the pythreejs.Scene object polylines: dict, optional Default {}. Mapping hexadecimal colors to a list of list(len(3)) representing the points of the polyline. Example: polylines={ "0xFFFFFF": [[0, 0, 0], [0, 0, 1]], "0xFF00FF": [[1, 0, 0], [1, 0, 1], [1, 1, 1]] } Returns ------- pythreejs.Scene if return_scene else None """ try: colors = self.points[use_as_color].values except KeyError: colors = None if use_as_color != ["red", "green", "blue"] and colors is not None: import matplotlib.pyplot as plt s_m = plt.cm.ScalarMappable(cmap=cmap) colors = s_m.to_rgba(colors)[:, :-1] * 255 elif colors is None: # default color orange colors = np.repeat([[255, 125, 0]], self.xyz.shape[0], axis=0) colors = colors.astype(np.uint8) ptp = self.xyz.ptp() if backend == "pythreejs": import ipywidgets import pythreejs from IPython.display import display if mesh: raise NotImplementedError( "Plotting mesh geometry with pythreejs backend is not supported yet." ) if polylines: raise NotImplementedError( "Plotting polylines with pythreejs backend is not supported yet." ) points_geometry = pythreejs.BufferGeometry(attributes=dict( position=pythreejs.BufferAttribute(self.xyz, normalized=False), color=pythreejs.BufferAttribute(list(map(tuple, colors))))) points_material = pythreejs.PointsMaterial( vertexColors='VertexColors') points = pythreejs.Points(geometry=points_geometry, material=points_material, position=tuple(self.centroid)) camera = pythreejs.PerspectiveCamera( fov=90, aspect=width / height, position=tuple( self.centroid + [0, abs(self.xyz.max(0)[1]), abs(self.xyz.max(0)[2]) * 2]), up=[0, 0, 1]) orbit_control = pythreejs.OrbitControls(controlling=camera) orbit_control.target = tuple(self.centroid) camera.lookAt(tuple(self.centroid)) scene = pythreejs.Scene(children=[points, camera], background=background) renderer = pythreejs.Renderer(scene=scene, camera=camera, controls=[orbit_control], width=width, height=height) display(renderer) size = ipywidgets.FloatSlider(min=0., max=(ptp / 100), step=(ptp / 1000)) ipywidgets.jslink((size, 'value'), (points_material, 'size')) color = ipywidgets.ColorPicker() ipywidgets.jslink((color, 'value'), (scene, 'background')) display( ipywidgets.HBox(children=[ ipywidgets.Label('Background color:'), color, ipywidgets.Label('Point size:'), size ])) return scene if return_scene else None elif backend == "threejs": points = pd.DataFrame(self.xyz, columns=["x", "y", "z"]) for n, i in enumerate(["red", "green", "blue"]): points[i] = colors[:, n] new_PyntCloud = PyntCloud(points) if mesh and self.mesh is not None: new_PyntCloud.mesh = self.mesh[["v1", "v2", "v3"]] return plot_PyntCloud(new_PyntCloud, IFrame_shape=(width, height), point_size=ptp / 100, point_opacity=0.9, output_name=output_name, polylines=polylines) else: raise NotImplementedError( "{} backend is not supported".format(backend))