def edges_to_mesh(name, np_edge_vertices, np_edge_indices, edge_color, linewidth=1): """ :param name: :param np_edge_vertices: :param np_edge_indices: :param edge_color: :param linewidth: :return: :rtype: pythreejs.objects.LineSegments_autogen.LineSegments """ edge_geometry = BufferGeometry( attributes={ "position": BufferAttribute(np_edge_vertices), "index": BufferAttribute(np_edge_indices), }) edge_material = LineBasicMaterial(color=format_color(*edge_color), linewidth=linewidth) edge_geom = LineSegments( geometry=edge_geometry, material=edge_material, type="LinePieces", name=name, ) return edge_geom
def tessellate(self, compound, quality=0.1, angular_tolerance=0.1, debug=False): hash = id( compound ) # use python id instead of compound.HashCode(HASH_CODE_MAX) if self.objects.get(hash) is None: np_vertices, np_triangles, np_normals = tessellate( compound, quality, angular_tolerance) if np_normals.shape != np_vertices.shape: raise AssertionError("Wrong number of normals/shapes") shape_geometry = BufferGeometry( attributes={ "position": BufferAttribute(np_vertices), "index": BufferAttribute(np_triangles.ravel()), "normal": BufferAttribute(np_normals), }) if debug: print(f"| | | (Caching {hash})") self.objects[hash] = shape_geometry else: if debug: print(f"| | | (Taking {hash} from cache)") return self.objects[hash]
def faces_to_mesh(name, vertices, faces, colors, opacity=None): """ :param name: :param vertices: :param faces: :param colors: :param opacity: :return: """ geometry = BufferGeometry(attributes=dict( position=BufferAttribute(vertices, normalized=False), index=BufferAttribute(faces, normalized=False), color=BufferAttribute(colors), )) mat_atts = dict(vertexColors="VertexColors", side="DoubleSide") if opacity is not None: mat_atts["opacity"] = opacity mat_atts["transparent"] = True material = MeshBasicMaterial(**mat_atts) mesh = Mesh( name=name, geometry=geometry, material=material, ) return mesh
def AddCurveToScene(self, shp, edge_color, deflection): """ shp is either a TopoDS_Wire or a TopodS_Edge. """ if is_edge(shp): pnts = discretize_edge(shp, deflection) elif is_wire(shp): pnts = discretize_wire(shp, deflection) np_edge_vertices = np.array(pnts, dtype=np.float32) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) edge_geometry = BufferGeometry( attributes={ 'position': BufferAttribute(np_edge_vertices), 'index': BufferAttribute(np_edge_indices) }) edge_material = LineBasicMaterial(color=edge_color, linewidth=1) # and to the dict of shapes, to have a mapping between meshes and shapes edge_id = "%s" % uuid.uuid4().hex self._shapes[edge_id] = shp edge_line = Line(geometry=edge_geometry, material=edge_material, name=edge_id) # and to the dict of shapes, to have a mapping between meshes and shapes edge_id = "%s" % uuid.uuid4().hex self._shapes[edge_id] = shp return edge_line
def create_face_geom( coord_vals: SoCoordValsListType, face_indices: SoIndicesListType, face_color: SoVectorType, transparency: float, translation: SoVectorType = None, quaternion: SoQuaternionType = None ) -> ThreeJSSceneGraphObjectListType: """ Returns a pythreejs `Mesh` object that consists of the faces given by face_indices and the coord_vals. Additionally the attributes `Mesh.default_material` and `Mesh.geometry.default_color` will be set before returning the Mesh. Those attributes contain references to the default colors and materials that are set in this function to restore later changes. """ vertices = np.asarray(coord_vals, dtype='float32') faces = np.asarray(face_indices, dtype='uint16') normals = compute_normals(faces, vertices) faces = faces.ravel() vertexcolors = np.asarray([face_color] * len(coord_vals), dtype='float32') face_geometry = BufferGeometry( attributes=dict(position=BufferAttribute(vertices, normalized=False), index=BufferAttribute(faces, normalized=False), normal=BufferAttribute(normals, normalized=False), color=BufferAttribute(vertexcolors, normalized=False))) # this is used for returning to original state after highlighting face_geometry.default_color = vertexcolors # BUG: This is a bug in pythreejs and currently does not work #faceGeometry.exec_three_obj_method('computeFaceNormals') #faceGeometry.exec_three_obj_method('computeVertexNormals') col = so_col_to_hex(face_color) material = MeshPhongMaterial(color=col, transparency=transparency, depthTest=True, depthWrite=True, metalness=0) object_mesh = Mesh( geometry=face_geometry, material=material, position=[0, 0, 0] # Center the cube ) object_mesh.default_material = material if quaternion: object_mesh.quaternion = quaternion if translation: object_mesh.position = translation return [object_mesh]
def occ_shape_to_threejs(self, shp: TopoDS_Shape, shape_color, edge_color, transparency, opacity): # first, compute the tesselation from .renderer_occ import occ_shape_to_faces from .threejs_utils import create_material np_vertices, np_faces, np_normals, edges = occ_shape_to_faces( shp, self.quality, self.render_edges, self.parallel) # set geometry properties buffer_geometry_properties = { "position": BufferAttribute(np_vertices), "index": BufferAttribute(np_faces), } if self.compute_normals_mode == NORMAL.SERVER_SIDE: if np_normals.shape != np_vertices.shape: raise AssertionError("Wrong number of normals/shapes") buffer_geometry_properties["normal"] = BufferAttribute(np_normals) # build a BufferGeometry instance shape_geometry = BufferGeometry(attributes=buffer_geometry_properties) # if the client has to render normals, add the related js instructions if self.compute_normals_mode == NORMAL.CLIENT_SIDE: shape_geometry.exec_three_obj_method("computeVertexNormals") # then a default material shp_material = create_material(shape_color, transparent=transparency, opacity=opacity) # and to the dict of shapes, to have a mapping between meshes and shapes mesh_id = "%s" % uuid.uuid4().hex self.mesh_id = mesh_id # finally create the mesh shape_mesh = Mesh(geometry=shape_geometry, material=shp_material, name=mesh_id) # edge rendering, if set to True if self.render_edges: edge_list = flatten(list(map(explode, edges))) lines = LineSegmentsGeometry(positions=edge_list) mat = LineMaterial(linewidth=1, color=edge_color) edge_lines = LineSegments2(lines, mat, name=mesh_id) else: edge_lines = None return shape_mesh, edge_lines
def _load_mesh(self, dae, scale): materials = self._load_material(dae) for geometry in dae.geometries: for primitive in geometry.primitives: vertices = primitive.vertex[primitive.vertex_index] * scale normals = primitive.normal[primitive.normal_index] buffer_geometry = BufferGeometry( attributes={ 'position': BufferAttribute(array=vertices), 'normal': BufferAttribute(array=normals) }) material = materials[primitive.material] mesh = Mesh(geometry=buffer_geometry, material=material) self.add(mesh)
def AddVerticesToScene(self, pnt_list, vertex_color, vertex_width=5): """ shp is a list of gp_Pnt """ vertices_list = [] # will be passed to pythreejs BB = BRep_Builder() compound = TopoDS_Compound() BB.MakeCompound(compound) for vertex in pnt_list: vertex_to_add = BRepBuilderAPI_MakeVertex(vertex).Shape() BB.Add(compound, vertex_to_add) vertices_list.append([vertex.X(), vertex.Y(), vertex.Z()]) # map the Points and the AIS_PointCloud # and to the dict of shapes, to have a mapping between meshes and shapes point_cloud_id = "%s" % uuid.uuid4().hex self._shapes[point_cloud_id] = compound vertices_list = np.array(vertices_list, dtype=np.float32) attributes = { "position": BufferAttribute(vertices_list, normalized=False) } mat = PointsMaterial(color=vertex_color, sizeAttenuation=True, size=vertex_width) geom = BufferGeometry(attributes=attributes) points = Points(geometry=geom, material=mat, name=point_cloud_id) return points
def animate_mesh(mesh, norm_displ_verts, displ_time, displ_magn): from pythreejs import ( AnimationAction, AnimationClip, AnimationMixer, NumberKeyframeTrack, ) mesh.morphAttributes = { "position": [ BufferAttribute(norm_displ_verts), ] } morphed_mesh = Mesh( mesh, MeshPhongMaterial(color="#ff3333", shininess=150, morphTargets=True)) pill_track = NumberKeyframeTrack(name=".morphTargetInfluences[0]", times=displ_time, values=displ_magn) pill_clip = AnimationClip(tracks=[pill_track]) pill_action = AnimationAction(AnimationMixer(morphed_mesh), pill_clip, morphed_mesh) return pill_action
def __init__(self, points, point_size=0.001, color='green'): geometry = BufferGeometry( attributes={ 'position': BufferAttribute(array=points.astype(np.float32)) }) material = PointsMaterial(size=point_size, color=color) Points.__init__(self, geometry=geometry, material=material)
def create_line_geom( coord_vals: SoCoordValsListType, indices: SoIndicesListType, line_color: SoVectorType, translation: SoVectorType = None, quaternion: SoQuaternionType = None ) -> ThreeJSSceneGraphObjectListType: """ Return a pythreejs Line object consisting of lines defined by the line_indices and the coord_vals. Additionally the attributes `Line.default_color` and `Line.edge_index` will be set before returning the `Line` array. Those attributes contain references to the default colors, that are set in this function to restore later changes, and the FreeCAD edge index. """ lines = [] for i, line_indices in enumerate(indices): vertices = generate_line_vertices(line_indices, coord_vals) vertices = np.array(vertices, dtype="float32") line_geom = BufferGeometry(attributes=dict( position=BufferAttribute(vertices, normalized=False))) # BUG: This is a bug in pythreejs and currently does not work #linesgeom.exec_three_obj_method('computeVertexNormals') col = so_col_to_hex(line_color) material = LineBasicMaterial(linewidth=LINE_WIDTH, color=col) material.default_color = col line = Line(geometry=line_geom, material=material) line.edge_index = i + 1 # using FreeCAD's 1-based indexing if translation: line.position = translation if quaternion: line.quaternion = quaternion lines.append(line) return lines
def AddCurveToScene(self, shp, color): """ shp is either a TopoDS_Wire or a TopodS_Edge. """ if is_edge(shp): pnts = discretize_edge(shp) elif is_wire(shp): pnts = discretize_wire(shp) np_edge_vertices = np.array(pnts, dtype=np.float32) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) edge_geometry = BufferGeometry(attributes={ 'position': BufferAttribute(np_edge_vertices), 'index' : BufferAttribute(np_edge_indices) }) edge_material = LineBasicMaterial(color=color, linewidth=1) edge_lines = Line(geometry=edge_geometry, material=edge_material) # Add geometries to pickable or non pickable objects self._displayed_pickable_objects.add(edge_lines)
def __init__(self, start, end, color='white', linewidth=1): geometry = BufferGeometry( attributes={ 'position': BufferAttribute(np.vstack((start, end)).astype(np.float32), normalized=False) }) material = LineBasicMaterial(color=color, linewidth=linewidth) pythreejs.Line.__init__(self, geometry=geometry, material=material)
def __init__(self, p1, p2, p3, color='yellow'): geometry = BufferGeometry( attributes={ 'position': BufferAttribute(np.vstack(( p1, p2, p3)).reshape(3, 3).astype(np.float32), normalized=False) }) material = MeshBasicMaterial(color=color) material.side = 'DoubleSide' Mesh.__init__(self, geometry=geometry, material=material)
def _get_surface_from_positions(positions, d_args, draw_edges=False): # get defaults obj_args = update_object_args(d_args, "Surfaces", ["color", "opacity"]) num_triangle = len(positions) / 3.0 assert num_triangle.is_integer() # make decision on transparency if obj_args["opacity"] > 0.99: transparent = False else: transparent = True num_triangle = int(num_triangle) index_list = [[itr * 3, itr * 3 + 1, itr * 3 + 2] for itr in range(num_triangle)] # Vertex ositions as a list of lists surf_vertices = BufferAttribute(array=positions, normalized=False) # Indices surf_indices = BufferAttribute(array=np.array(index_list, dtype=np.uint16).ravel(), normalized=False) geometry = BufferGeometry(attributes={ "position": surf_vertices, "index": surf_indices }) new_surface = Mesh( geometry=geometry, material=MeshLambertMaterial( color=obj_args["color"], side="DoubleSide", transparent=transparent, opacity=obj_args["opacity"], ), ) if draw_edges == True: edges = EdgesGeometry(geometry) edges_lines = LineSegments(edges, LineBasicMaterial(color=obj_args["color"])) return new_surface, edges_lines else: return new_surface, None
def vertices_to_mesh(name, vertices, vertex_color, vertex_width=5): """ :param name: :param vertices: :param vertex_color: RGB tuple (r, g, b) :param vertex_width: :return: :rtype: pythreejs.objects.Points_autogen.Points """ vertices = np.array(vertices, dtype=np.float32) attributes = {"position": BufferAttribute(vertices, normalized=False)} mat = PointsMaterial(color=format_color(*vertex_color), sizeAttenuation=False, size=vertex_width) geom = BufferGeometry(attributes=attributes) points = Points(geometry=geom, material=mat, name=name) return points
def _render_shape( self, shape_index, shape=None, edges=None, vertices=None, mesh_color=None, edge_color=None, vertex_color=None, render_edges=False, edge_width=1, vertex_width=5, transparent=False, opacity=1.0, ): edge_list = None edge_lines = None points = None shape_mesh = None start_render_time = self._start_timer() if shape is not None: if mesh_color is None: mesh_color = self.default_mesh_color if edge_color is None: edge_color = self.default_edge_color if vertex_color is None: vertex_color = self.default_edge_color # same as edge_color # Compute the tesselation start_tesselation_time = self._start_timer() np_vertices, np_triangles, np_normals = tessellate( shape, self.quality, self.angular_tolerance ) if np_normals.shape != np_vertices.shape: raise AssertionError("Wrong number of normals/shapes") self._stop_timer("tesselation time", start_tesselation_time) # build a BufferGeometry instance shape_geometry = BufferGeometry( attributes={ "position": BufferAttribute(np_vertices), "index": BufferAttribute(np_triangles.ravel()), "normal": BufferAttribute(np_normals), } ) shp_material = self._material(mesh_color, transparent=True, opacity=opacity) shape_mesh = Mesh( geometry=shape_geometry, material=shp_material, name="mesh_%d" % shape_index ) if render_edges: edges = get_edges(shape) if vertices is not None: vertices_list = [] for vertex in vertices: vertices_list.append(get_point(vertex)) vertices_list = np.array(vertices_list, dtype=np.float32) attributes = {"position": BufferAttribute(vertices_list, normalized=False)} mat = PointsMaterial(color=vertex_color, sizeAttenuation=False, size=vertex_width) geom = BufferGeometry(attributes=attributes) points = Points(geometry=geom, material=mat) if edges is not None: start_discretize_time = self._start_timer() edge_list = [discretize_edge(edge, self.edge_accuracy) for edge in edges] self._stop_timer("discretize time", start_discretize_time) if edge_list is not None: edge_list = flatten(list(map(explode, edge_list))) lines = LineSegmentsGeometry(positions=edge_list) mat = LineMaterial(linewidth=edge_width, color=edge_color) edge_lines = LineSegments2(lines, mat, name="edges_%d" % shape_index) if shape_mesh is not None or edge_lines is not None or points is not None: index_mapping = {"mesh": None, "edges": None, "shape": shape_index} if shape_mesh is not None: ind = len(self.pickable_objects.children) self.pickable_objects.add(shape_mesh) index_mapping["mesh"] = ind if edge_lines is not None: ind = len(self.pickable_objects.children) self.pickable_objects.add(edge_lines) index_mapping["edges"] = ind if points is not None: ind = len(self.pickable_objects.children) self.pickable_objects.add(points) index_mapping["mesh"] = ind self.pick_mapping.append(index_mapping) self._stop_timer("shape render time", start_render_time)
def __init__(self, mesh: trimesh.Trimesh = None, obj: str = None, normalize: bool =False, color: Union[str,ColorRGB] ="#00bbee", alpha: float=1., transform: Union[Callable, None] = None, *args, **kwargs ): """ Add a mesh to the scene. Arguments: mesh: Mesh object, with attributes verticies, faces obj: object filename normalize : Normalize the coordinates of the vertices to be between -1 and 1 color: Color for the mesh alpha: transparency of the mesh transform: a function to transform the vertices """ if mesh is not None and obj is not None: raise ValueError('Received both mesh and obj') if isinstance(mesh, str): # perhaps this is a filename? try: mesh = trimesh.load(args[0]) except Exception as e: raise ValueError( "Did not understand arguments to method Figure#mesh" ) from e if isinstance(obj, np.ndarray): obj_data = obj elif isinstance(obj, list): obj_data = np.asarray(obj) # Do something with this obj_data? elif isinstance(obj, str): if "\n" in obj: # this is the file contents. raise NotImplementedError() else: try: # open the mesh file mesh = trimesh.load(obj) except Exception as e: raise ValueError("Could not read file as OBJ") from e assert hasattr(mesh, "vertices") and hasattr(mesh, "faces"), "Invalid mesh object" if mesh is None: raise ValueError("Could not understand how to parse mesh.") if transform is None: transform = lambda x: x verts = transform(mesh.vertices) faces = mesh.faces if normalize: # Normalize the vertex indices to be between -1,1 # Shifting these does change the coordinate system, # so visualizing multiple meshes won't work verts[:, 0] = _normalize_shift(verts[:, 0]) verts[:, 1] = _normalize_shift(verts[:, 1]) verts[:, 2] = _normalize_shift(verts[:, 2]) geo = BufferGeometry( attributes={ "position": BufferAttribute( array=verts.astype("float32"), normalized=False, # dtype=np.float64 ), "index": BufferAttribute( array=faces.astype("uint64").ravel(), normalized=False, # dtype=np.float64, ), } ) self._coords = verts transparent = alpha != 1. mat = MeshLambertMaterial(color=color, opacity=alpha, transparent=transparent) mesh = Mesh(geometry=geo, material=mat) geo.exec_three_obj_method("computeVertexNormals") super().__init__(*args, **kwargs) self._objects.append(mesh)
def DisplayMesh(self, part, edge_color=None, vertex_color=None, vertex_width=2): """ :param part: :param edge_color: :param vertex_color: :param vertex_width: :type part: ada.Part """ from itertools import chain from random import randint from OCC.Core.BRep import BRep_Builder from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeVertex from OCC.Core.gp import gp_Pnt from OCC.Core.TopoDS import TopoDS_Compound # edge_color = format_color(*part.colour) if edge_color is None else edge_color edge_color = (format_color(randint(0, 255), randint( 0, 255), randint(0, 255)) if edge_color is None else edge_color) vertex_color = self._default_vertex_color if vertex_color is None else vertex_color pmesh_id = "%s" % uuid.uuid4().hex BB = BRep_Builder() compound = TopoDS_Compound() BB.MakeCompound(compound) vertices_list = [] def togp(n_): return gp_Pnt(float(n_[0]), float(n_[1]), float(n_[2])) for vertex in map(togp, part.fem.nodes): vertex_to_add = BRepBuilderAPI_MakeVertex(vertex).Shape() BB.Add(compound, vertex_to_add) vertices_list.append([vertex.X(), vertex.Y(), vertex.Z()]) attributes = { "position": BufferAttribute(vertices_list, normalized=False) } mat = PointsMaterial(color=vertex_color, sizeAttenuation=False, size=vertex_width) geom = BufferGeometry(attributes=attributes) points_geom = Points(geometry=geom, material=mat, name=pmesh_id) def grab_nodes(el): """ :param el: :type el: ada.fem.Elem :return: """ if el.edges_seq is None: return None return [ part.fem.nodes.from_id(i).p for i in [el.nodes[e].id for ed_seq in el.edges_seq for e in ed_seq] ] lmesh_id = "%s" % uuid.uuid4().hex edges_nodes = list( chain.from_iterable( filter(None, map(grab_nodes, part.fem.elements)))) np_edge_vertices = np.array(edges_nodes, dtype=np.float32) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) edge_geometry = BufferGeometry( attributes={ "position": BufferAttribute(np_edge_vertices), "index": BufferAttribute(np_edge_indices), }) edge_material = LineBasicMaterial(color=edge_color, linewidth=1) edge_geom = LineSegments( geometry=edge_geometry, material=edge_material, type="LinePieces", name=lmesh_id, ) output = [points_geom, edge_geom] for elem in output: self._shapes[elem.name] = compound self._refs[elem.name] = part self._displayed_pickable_objects.add(elem) self._fem_sets_opts.options = ["None"] + [ s.name for s in filter( lambda x: "internal" not in x.metadata.keys(), part.fem.sets) ]
def AddShapeToScene( self, shp, shape_color=None, # the default render_edges=False, edge_color=None, vertex_color=None, quality=1.0, transparency=False, opacity=1.0, ): # first, compute the tesselation tess = ShapeTesselator(shp) tess.Compute(compute_edges=render_edges, mesh_quality=quality, parallel=True) # get vertices and normals vertices_position = tess.GetVerticesPositionAsTuple() number_of_triangles = tess.ObjGetTriangleCount() number_of_vertices = len(vertices_position) # number of vertices should be a multiple of 3 if number_of_vertices % 3 != 0: raise AssertionError("Wrong number of vertices") if number_of_triangles * 9 != number_of_vertices: raise AssertionError("Wrong number of triangles") # then we build the vertex and faces collections as numpy ndarrays np_vertices = np.array(vertices_position, dtype="float32").reshape( int(number_of_vertices / 3), 3) # Note: np_faces is just [0, 1, 2, 3, 4, 5, ...], thus arange is used np_faces = np.arange(np_vertices.shape[0], dtype="uint32") # set geometry properties buffer_geometry_properties = { "position": BufferAttribute(np_vertices), "index": BufferAttribute(np_faces), } if self._compute_normals_mode == NORMAL.SERVER_SIDE: # get the normal list, converts to a numpy ndarray. This should not raise # any issue, since normals have been computed by the server, and are available # as a list of floats np_normals = np.array(tess.GetNormalsAsTuple(), dtype="float32").reshape(-1, 3) # quick check if np_normals.shape != np_vertices.shape: raise AssertionError("Wrong number of normals/shapes") buffer_geometry_properties["normal"] = BufferAttribute(np_normals) # build a BufferGeometry instance shape_geometry = BufferGeometry(attributes=buffer_geometry_properties) # if the client has to render normals, add the related js instructions if self._compute_normals_mode == NORMAL.CLIENT_SIDE: shape_geometry.exec_three_obj_method("computeVertexNormals") # then a default material shp_material = self._material(shape_color, transparent=transparency, opacity=opacity) # and to the dict of shapes, to have a mapping between meshes and shapes mesh_id = "%s" % uuid.uuid4().hex self._shapes[mesh_id] = shp # finally create the mesh shape_mesh = Mesh(geometry=shape_geometry, material=shp_material, name=mesh_id) # edge rendering, if set to True if render_edges: edges = list( map( lambda i_edge: [ tess.GetEdgeVertex(i_edge, i_vert) for i_vert in range(tess.ObjEdgeGetVertexCount(i_edge)) ], range(tess.ObjGetEdgeCount()), )) edge_list = _flatten(list(map(_explode, edges))) lines = LineSegmentsGeometry(positions=edge_list) mat = LineMaterial(linewidth=1, color=edge_color) edge_lines = LineSegments2(lines, mat, name=mesh_id) self._displayed_non_pickable_objects.add(edge_lines) return shape_mesh
def DisplayMesh(self, part: "Part", edge_color=None, vertex_color=None, vertex_width=2): from OCC.Core.BRep import BRep_Builder from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeVertex from OCC.Core.gp import gp_Pnt from OCC.Core.TopoDS import TopoDS_Compound # edge_color = format_color(*part.colour) if edge_color is None else edge_color rgb = randint(0, 255), randint(0, 255), randint(0, 255) edge_color = format_color(*rgb) if edge_color is None else edge_color vertex_color = self._default_vertex_color if vertex_color is None else vertex_color pmesh_id = "%s" % uuid.uuid4().hex BB = BRep_Builder() compound = TopoDS_Compound() BB.MakeCompound(compound) vertices_list = [] def togp(n_): return gp_Pnt(float(n_[0]), float(n_[1]), float(n_[2])) for vertex in map(togp, part.fem.nodes): vertex_to_add = BRepBuilderAPI_MakeVertex(vertex).Shape() BB.Add(compound, vertex_to_add) vertices_list.append([vertex.X(), vertex.Y(), vertex.Z()]) attributes = { "position": BufferAttribute(vertices_list, normalized=False) } mat = PointsMaterial(color=vertex_color, sizeAttenuation=False, size=vertex_width) geom = BufferGeometry(attributes=attributes) points_geom = Points(geometry=geom, material=mat, name=pmesh_id) lmesh_id = "%s" % uuid.uuid4().hex edges_nodes = list( chain.from_iterable( filter( None, [get_vertices_from_elem(el) for el in part.fem.elements]))) np_edge_vertices = np.array(edges_nodes, dtype=np.float32) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) vertex_col = tuple([x / 255 for x in rgb]) edge_geometry = BufferGeometry( attributes={ "position": BufferAttribute(np_edge_vertices), "index": BufferAttribute(np_edge_indices), "color": BufferAttribute( [vertex_col for n in np_edge_vertices]), }) edge_material = LineBasicMaterial(vertexColors="VertexColors", linewidth=5) edge_geom = LineSegments( geometry=edge_geometry, material=edge_material, type="LinePieces", name=lmesh_id, ) output = [points_geom, edge_geom] for elem in output: self._shapes[elem.name] = compound self._refs[elem.name] = part self._displayed_pickable_objects.add(elem) self._fem_sets_opts.options = ["None"] + [ f"{part.fem.name}.{s.name}" for s in filter( lambda x: "internal" not in x.metadata.keys(), part.fem.sets) ] self._fem_refs[part.fem.name] = (part.fem, edge_geometry)
def render_shape( self, shape=None, edges=None, vertices=None, mesh_color=None, edge_color=None, vertex_color=None, render_edges=True, render_shapes=True, edge_width=1, vertex_width=5, transparent=False, opacity=1.0, ): edge_list = None edge_lines = None points = None shape_mesh = None render_timer = Timer(self.timeit, "| | shape render time") if shape is not None: if mesh_color is None: mesh_color = self.default_mesh_color if edge_color is None: edge_color = self.default_edge_color if vertex_color is None: vertex_color = self.default_edge_color # same as edge_color # Compute the tesselation and build mesh tesselation_timer = Timer(self.timeit, "| | | build mesh time") shape_geometry = RENDER_CACHE.tessellate(shape, self.quality, self.angular_tolerance, self.timeit) shp_material = material(mesh_color.web_color, transparent=transparent, opacity=opacity) # Do not cache building the mesh. Might lead to unpredictable results shape_mesh = IndexedMesh(geometry=shape_geometry, material=shp_material) tesselation_timer.stop() if render_edges: edges = get_edges(shape) # unset shape_mesh again if not render_shapes: shape_mesh = None if vertices is not None: vertices_list = [] for vertex in vertices: vertices_list.append(get_point(vertex)) vertices_list = np.array(vertices_list, dtype=np.float32) attributes = { "position": BufferAttribute(vertices_list, normalized=False) } mat = PointsMaterial(color=vertex_color.web_color, sizeAttenuation=False, size=vertex_width) geom = BufferGeometry(attributes=attributes) points = IndexedPoints(geometry=geom, material=mat) if edges is not None: discretize_timer = Timer(self.timeit, "| | | discretize time") edge_list = [ discretize_edge(edge, self.edge_accuracy) for edge in edges ] discretize_timer.stop() if edge_list is not None: discretize_timer = Timer(self.timeit, "| | | edge list") edge_list = flatten(list(map(explode, edge_list))) if isinstance(edge_color, (list, tuple)): if len(edge_list) != len(edge_color): print( "warning: color list and edge list have different length, using first color for all edges" ) edge_color = edge_color[0] if isinstance(edge_color, (list, tuple)): lines = LineSegmentsGeometry( positions=edge_list, colors=[[color.percentage] * 2 for color in edge_color], ) mat = LineMaterial(linewidth=edge_width, vertexColors="VertexColors") edge_lines = [IndexedLineSegments2(lines, mat)] else: lines = LineSegmentsGeometry(positions=edge_list) mat = LineMaterial(linewidth=edge_width, color=edge_color.web_color) edge_lines = [IndexedLineSegments2(lines, mat)] discretize_timer.stop() render_timer.stop() return shape_mesh, edge_lines, points
def _render_shape( self, shape=None, edges=None, vertices=None, mesh_color=None, edge_color=None, vertex_color=None, edge_width=1, vertex_width=5, transparent=False, opacity=1.0, ): edge_list = [] normals_list = [] edge_lines = [] normal_lines = [] points = None shape_mesh = None # edge_accuracy = None if shape is not None: # Compute the tesselation and build mesh with Timer(self.timeit, "", "build mesh:", 5): edge_list, normals_list = shape["edges"] shape_geometry = BufferGeometry( attributes={ "position": BufferAttribute(shape["vertices"]), "index": BufferAttribute(shape["triangles"]), "normal": BufferAttribute(shape["normals"]), } ) if mesh_color is None: mesh_color = self.default_mesh_color shp_material = material(mesh_color, transparent=transparent, opacity=opacity) shape_mesh = IndexedMesh(geometry=shape_geometry, material=shp_material) if vertices is not None: if vertex_color is None: vertex_color = self.default_edge_color # same as edge_color vertices_list = vertices attributes = {"position": BufferAttribute(vertices_list, normalized=False)} mat = PointsMaterial(color=vertex_color, sizeAttenuation=False, size=vertex_width) geom = BufferGeometry(attributes=attributes) points = IndexedPoints(geometry=geom, material=mat) if edges is not None: edge_list = edges if len(edge_list) > 0: if edge_color is None: edge_color = self.default_edge_color if isinstance(edge_color, (list, tuple)): if len(edge_list) != len(edge_color): print("warning: color list and edge list have different length, using first color for all edges") edge_color = edge_color[0] if isinstance(edge_color, (list, tuple)): lines = LineSegmentsGeometry( positions=edge_list, colors=[[Color(color).percentage] * 2 for color in edge_color], ) mat = LineMaterial(linewidth=edge_width, vertexColors="VertexColors") edge_lines = [IndexedLineSegments2(lines, mat)] else: lines = LineSegmentsGeometry(positions=edge_list) mat = LineMaterial( linewidth=edge_width, color=edge_color.web_color if isinstance(edge_color, Color) else edge_color ) edge_lines = [IndexedLineSegments2(lines, mat)] if len(normals_list) > 0: lines = LineSegmentsGeometry(positions=normals_list) mat = LineMaterial(linewidth=2, color="#9400d3") normal_lines = [IndexedLineSegments2(lines, mat)] return shape_mesh, edge_lines, normal_lines, points
def _render_shape(self, shape_index, shape=None, edges=None, vertices=None, mesh_color=None, edge_color=None, vertex_color=None, render_edges=False, edge_width=1, vertex_width=5, deflection=0.05, transparent=False, opacity=1.0): edge_list = None edge_lines = None points = None shape_mesh = None if shape is not None: if mesh_color is None: mesh_color = self.default_mesh_color if edge_color is None: edge_color = self.default_edge_color if vertex_color is None: vertex_color = self.default_edge_color # same as edge_color # BEGIN copy # The next lines are copied with light modifications from # https://github.com/tpaviot/pythonocc-core/blob/master/src/Display/WebGl/jupyter_renderer.py # first, compute the tesselation tess = Tesselator(shape) tess.Compute(uv_coords=False, compute_edges=render_edges, mesh_quality=self.quality, parallel=True) # get vertices and normals vertices_position = tess.GetVerticesPositionAsTuple() number_of_triangles = tess.ObjGetTriangleCount() number_of_vertices = len(vertices_position) # number of vertices should be a multiple of 3 if number_of_vertices % 3 != 0: raise AssertionError("Wrong number of vertices") if number_of_triangles * 9 != number_of_vertices: raise AssertionError("Wrong number of triangles") # then we build the vertex and faces collections as numpy ndarrays np_vertices = np.array(vertices_position, dtype='float32')\ .reshape(int(number_of_vertices / 3), 3) # Note: np_faces is just [0, 1, 2, 3, 4, 5, ...], thus arange is used np_faces = np.arange(np_vertices.shape[0], dtype='uint32') # compute normals np_normals = np.array(tess.GetNormalsAsTuple(), dtype='float32').reshape(-1, 3) if np_normals.shape != np_vertices.shape: raise AssertionError("Wrong number of normals/shapes") # build a BufferGeometry instance shape_geometry = BufferGeometry( attributes={ 'position': BufferAttribute(np_vertices), 'index': BufferAttribute(np_faces), 'normal': BufferAttribute(np_normals) }) shp_material = self._material(mesh_color, transparent=True, opacity=opacity) shape_mesh = Mesh(geometry=shape_geometry, material=shp_material, name="mesh_%d" % shape_index) if render_edges: edge_list = list( map( lambda i_edge: [ tess.GetEdgeVertex(i_edge, i_vert) for i_vert in range( tess.ObjEdgeGetVertexCount(i_edge)) ], range(tess.ObjGetEdgeCount()))) # END copy if vertices is not None: vertices_list = [] for vertex in vertices: p = BRep_Tool.Pnt(vertex) vertices_list.append((p.X(), p.Y(), p.Z())) vertices_list = np.array(vertices_list, dtype=np.float32) attributes = { "position": BufferAttribute(vertices_list, normalized=False) } mat = PointsMaterial(color=vertex_color, sizeAttenuation=False, size=vertex_width) geom = BufferGeometry(attributes=attributes) points = Points(geometry=geom, material=mat) if edges is not None: edge_list = [discretize_edge(edge, deflection) for edge in edges] if edge_list is not None: edge_list = _flatten(list(map(_explode, edge_list))) lines = LineSegmentsGeometry(positions=edge_list) mat = LineMaterial(linewidth=edge_width, color=edge_color) edge_lines = LineSegments2(lines, mat, name="edges_%d" % shape_index) if shape_mesh is not None or edge_lines is not None or points is not None: index_mapping = {"mesh": None, "edges": None, "shape": shape_index} if shape_mesh is not None: ind = len(self.pickable_objects.children) self.pickable_objects.add(shape_mesh) index_mapping["mesh"] = ind if edge_lines is not None: ind = len(self.pickable_objects.children) self.pickable_objects.add(edge_lines) index_mapping["edges"] = ind if points is not None: ind = len(self.pickable_objects.children) self.pickable_objects.add(points) index_mapping["mesh"] = ind self.pick_mapping.append(index_mapping)
def AddShapeToScene(self, shp, # the TopoDS_Shape to be displayed shape_color=default_shape_color, # the default render_edges=False, edge_color=default_edge_color, compute_uv_coords=False, quality=1.0, transparency=False, opacity=1.): # first, compute the tesselation tess = Tesselator(shp) tess.Compute(uv_coords=compute_uv_coords, compute_edges=render_edges, mesh_quality=quality, parallel=self._parallel) # get vertices and normals vertices_position = tess.GetVerticesPositionAsTuple() number_of_triangles = tess.ObjGetTriangleCount() number_of_vertices = len(vertices_position) # number of vertices should be a multiple of 3 if number_of_vertices % 3 != 0: raise AssertionError("Wrong number of vertices") if number_of_triangles * 9 != number_of_vertices: raise AssertionError("Wrong number of triangles") # then we build the vertex and faces collections as numpy ndarrays np_vertices = np.array(vertices_position, dtype='float32').reshape(int(number_of_vertices / 3), 3) # Note: np_faces is just [0, 1, 2, 3, 4, 5, ...], thus arange is used np_faces = np.arange(np_vertices.shape[0], dtype='uint32') # set geometry properties buffer_geometry_properties = {'position': BufferAttribute(np_vertices), 'index' : BufferAttribute(np_faces)} if self._compute_normals_mode == NORMAL.SERVER_SIDE: # get the normal list, converts to a numpy ndarray. This should not raise # any issue, since normals have been computed by the server, and are available # as a list of floats np_normals = np.array(tess.GetNormalsAsTuple(), dtype='float32').reshape(-1, 3) # quick check if np_normals.shape != np_vertices.shape: raise AssertionError("Wrong number of normals/shapes") buffer_geometry_properties['normal'] = BufferAttribute(np_normals) # build a BufferGeometry instance shape_geometry = BufferGeometry(attributes=buffer_geometry_properties) # if the client has to render normals, add the related js instructions if self._compute_normals_mode == NORMAL.CLIENT_SIDE: shape_geometry.exec_three_obj_method('computeVertexNormals') # then a default material shp_material = self._material(shape_color, transparent=transparency, opacity=opacity) # create a mesh unique id mesh_id = uuid.uuid4().hex # finally create the mash shape_mesh = Mesh(geometry=shape_geometry, material=shp_material, name=mesh_id) # and to the dict of shapes, to have a mapping between meshes and shapes self._shapes[mesh_id] = shp # edge rendering, if set to True edge_lines = None if render_edges: edges = list(map(lambda i_edge: [tess.GetEdgeVertex(i_edge, i_vert) for i_vert in range(tess.ObjEdgeGetVertexCount(i_edge))], range(tess.ObjGetEdgeCount()))) edges = list(filter(lambda edge: len(edge) == 2, edges)) np_edge_vertices = np.array(edges, dtype=np.float32).reshape(-1, 3) np_edge_indices = np.arange(np_edge_vertices.shape[0], dtype=np.uint32) edge_geometry = BufferGeometry(attributes={ 'position': BufferAttribute(np_edge_vertices), 'index' : BufferAttribute(np_edge_indices) }) edge_material = LineBasicMaterial(color=edge_color, linewidth=1) edge_lines = LineSegments(geometry=edge_geometry, material=edge_material) # Add geometries to pickable or non pickable objects self._displayed_pickable_objects.add(shape_mesh) if render_edges: self._displayed_non_pickable_objects.add(edge_lines)
def __init__(self,*args, **kwargs): """ There are several options for arguments this this function. One positional array-like Figure#scatter(np.random.randint(0, 10, (10, 3))) Three positional list-likes Figure#scatter(xs, ys, zs) Three named list-likes Figure#scatter(xs=[1, 2], ys=[2, 3], zs=[10, 20]) Three positional column names and a dataframe Figure#scatter("x", "y", "depth", my_dataframe) Arguments: attenuate_size (False): Whether items further from the camera should appear smaller """ super().__init__(**kwargs) pts = None if len(args) == 1: if isinstance(args[0], (np.ndarray, Iterable)): pts = np.asarray(args[0], dtype=np.float32) if len(args) == 3 and isinstance(args[0], (list, np.ndarray)): pts = np.asarray([i for i in zip(args[0], args[1], args[2])],dtype=np.float32) if pts is None: raise ValueError("Unsupported arguments to scatter.") self._coords = pts color = kwargs.get("c") if "c" in kwargs else None if color is None: color = kwargs.get("color", None) if color is None: color = pts / pts.max() if len(color) != len(pts): color = [color for _ in pts] colors = ( BufferAttribute(array=color, dtype=np.float32) if color is not None else BufferAttribute(array=np.asarray(pts / pts.max(), dtype=np.float32)) ) geometry = BufferGeometry( attributes={ "position": BufferAttribute(array=np.asarray(pts, dtype=np.float32)), "color": colors, } ) tex = CIRCLE_MAP if kwargs.get("marker") in [".", "o", "circle"]: tex = CIRCLE_MAP elif kwargs.get("marker") in ["[]", "r", "q", "square"]: tex = None elif "map" in kwargs: tex = kwargs.get("map") material = PointsMaterial( vertexColors="VertexColors", size=kwargs.get("size", 5), sizeAttenuation=kwargs.get("attenuate_size", False), **({"map": tex} if tex else {}), ) p = Points(geometry=geometry, material=material) self._objects.append(p)
def DisplayMesh(self, mesh, color=default_mesh_color): """ Display a MEFISTO2 triangle mesh """ if not HAVE_SMESH: print("SMESH not installed, DisplayMesh method unavailable.") return if not isinstance(mesh, SMESH_Mesh): raise AssertionError("You mush provide an SMESH_Mesh instance") mesh_ds = mesh.GetMeshDS() # the mesh data source face_iter = mesh_ds.facesIterator() # vertices positions are stored to a liste vertices_position = [] for _ in range(mesh_ds.NbFaces()-1): face = face_iter.next() #print('Face %i, type %i' % (i, face.GetType())) #print(dir(face)) # if face.GetType == 3 : triangle mesh, then 3 nodes for j in range(3): node = face.GetNode(j) #print('Coordinates of node %i:(%f,%f,%f)'%(i, node.X(), node.Y(), node.Z())) vertices_position.append(node.X()) vertices_position.append(node.Y()) vertices_position.append(node.Z()) number_of_vertices = len(vertices_position) # then we build the vertex and faces collections as numpy ndarrays np_vertices = np.array(vertices_position, dtype='float32').reshape(int(number_of_vertices / 3), 3) # Note: np_faces is just [0, 1, 2, 3, 4, 5, ...], thus arange is used np_faces = np.arange(np_vertices.shape[0], dtype='uint32') # set geometry properties buffer_geometry_properties = {'position': BufferAttribute(np_vertices), 'index' : BufferAttribute(np_faces)} # build a BufferGeometry instance mesh_geometry = BufferGeometry(attributes=buffer_geometry_properties) mesh_geometry.exec_three_obj_method('computeVertexNormals') # then a default material mesh_material = MeshPhongMaterial(color=color, polygonOffset=True, polygonOffsetFactor=1, polygonOffsetUnits=1, shininess=0.5, wireframe=False, side='DoubleSide') edges_material = MeshPhongMaterial(color='black', polygonOffset=True, polygonOffsetFactor=1, polygonOffsetUnits=1, shininess=0.5, wireframe=True) # create a mesh unique id mesh_id = uuid.uuid4().hex # finally create the mash shape_mesh = Mesh(geometry=mesh_geometry, material=mesh_material, name=mesh_id) edges_mesh = Mesh(geometry=mesh_geometry, material=edges_material, name=mesh_id) # a special display for the mesh camera_target = [0., 0., 0.] # the point to look at camera_position = [0, 0., 100.] # the camera initial position camera = PerspectiveCamera(position=camera_position, lookAt=camera_target, up=[0, 0, 1], fov=50, children=[DirectionalLight(color='#ffffff', position=[50, 50, 50], intensity=0.9)]) scene_shp = Scene(children=[shape_mesh, edges_mesh, camera, AmbientLight(color='#101010')]) renderer = Renderer(camera=camera, background=self._background, background_opacity=self._background_opacity, scene=scene_shp, controls=[OrbitControls(controlling=camera, target=camera_target)], width=self._size[0], height=self._size[1], antialias=True) display(renderer)