def callback_f(change): """ This functions implements highlighting and displaying the name of selected faces and edges as well as vertices. You seemingly can't select nothing, it only triggers on objects. """ value = picker.object value_freecad_name = freecad_name_from_obj3d(value) last_value = picker.last_object if value is None: if not (last_value is None): reset_object_highlighting(last_value) html.value = "<b>No selection.</b>" return if isinstance(value, Line): if not (last_value is None): reset_object_highlighting(last_value) value.material.color = so_col_to_hex(HIGHLIGHTING_COLOR) html.value = "{} <b>Edge{}</b>".format(value_freecad_name, value.edge_index) return face_index = int(picker.faceIndex) part_index = part_index_by_name(get_name(value), part_indices) shape_face_index = index_by_face_index(part_index, face_index) if not (last_value is None): # check for case of selecting the same freecad face if (last_value.name == value.name) and (picker.shape_face_index_old == shape_face_index): return reset_object_highlighting(last_value) face_indices = value.geometry.attributes["index"].array cols_default = value.geometry.default_color cols_highlighted = vertices_col_highlight_face(shape_face_index, cols_default, part_index, face_indices) value.geometry.attributes["color"].array = cols_highlighted value.geometry.attributes["color"].needsUpdate = True material = MeshLambertMaterial(vertexColors='VertexColors', shininess=1) material.name = value.material.color value.material = material picker.shape_face_index_old = shape_face_index picker.last_object = value html.value = "{} <b>Face{}</b>".format(value_freecad_name, shape_face_index)
def convert_object_to_pythreejs(object): """ Cases for the conversion :return: """ obs = [] if object["type"] == "spheres": for ipos in object["positions"]: obj3d = Mesh( geometry=SphereBufferGeometry(radius=object["radius"], widthSegments=32, heightSegments=16), material=MeshLambertMaterial(color=object["color"]), position=ipos, ) obs.append(obj3d) elif object["type"] == "cylinders": for ipos in object["positionPairs"]: obj3d = _get_cylinder_from_vec(ipos[0], ipos[1], color=object["color"]) obs.append(obj3d) elif object["type"] == "lines": for ipos, jpos in zip(object["positions"][::2], object["positions"][1::2]): obj3d = _get_line_from_vec(ipos, jpos) obs.append(obj3d) else: warnings.warn( f"Primitive type {object['type']} has not been implemented for this renderer." ) return obs
def virtualHelixMesh(pos1, pos2, radius: float = 1.0, color: str = 'red') -> Mesh: pos1 = np.array(pos1, dtype=float) pos2 = np.array(pos2, dtype=float) delta = pos2 - pos1 dist = np.linalg.norm(delta) c_geometry = CylinderBufferGeometry(radiusTop=radius, radiusBottom=radius, height=dist, radiusSegments=16) # print(c_geometry.height) v2 = normalize(delta) # NOTE default direction in Three.js is the Y direction for geometry v1 = np.array((0, 1, 0)) # Calculate angle between Vectors angle = math.atan2(np.dot(np.cross(v1, v2), v1), np.dot(v1, v2)) normal_vec = normalize(np.cross(v1, v2)).tolist() mid_point = (pos2 + pos1) / 2 xAxis = [mid_point[0], 0, 0] yAxis = [0, mid_point[1], 0] zAxis = [0, 0, mid_point[2]] mesh = Mesh(geometry=c_geometry, material=MeshLambertMaterial(color=color)) rotm = rotationMatrix(v2) mesh.setRotationFromMatrix(threeMatrix(rotm)) mesh.position = mid_point.tolist() return mesh, mid_point
def convert_object_to_pythreejs(object): """ Cases for the conversion :return: """ obs = [] if object['type'] == 'spheres': for ipos in object['positions']: obj3d = Mesh(geometry=SphereBufferGeometry(radius=object['radius'], widthSegments=32, heightSegments=16), material=MeshLambertMaterial(color=object["color"]), position=ipos) obs.append(obj3d) elif object['type'] == 'cylinders': for ipos in object['positionPairs']: obj3d = _get_cylinder_from_vec(ipos[0], ipos[1], color=object['color']) obs.append(obj3d) elif object['type'] == 'lines': for ipos, jpos in zip(object['positions'][::2], object['positions'][1::2]): obj3d = _get_line_from_vec(ipos, jpos) obs.append(obj3d) return obs
def pseudomaterial_render(atoms): c = cm.get_cmap("plasma") scale_axis_vertices = [[-1, -1, -1], [9, -1, -1]] scale_line_geom = Geometry(vertices=scale_axis_vertices, colors=['black'] * len(scale_axis_vertices)) scale_lines = Line( geometry=scale_line_geom, material=LineBasicMaterial(linewidth=50, vertexColors='VertexColors'), type='LinePieces', ) a = atoms.a[1] cube_vertices = [[0, 0, 0], [a, 0, 0], [a, 0, a], [a, 0, 0], [a, a, 0], [a, a, a], [a, a, 0], [0, a, 0], [0, a, a], [0, a, 0], [0, 0, 0], [0, 0, a], [a, 0, a], [a, a, a], [0, a, a], [0, 0, a]] linesgeom = Geometry(vertices=cube_vertices, colors=['black'] * len(cube_vertices)) lines = Line( geometry=linesgeom, material=LineBasicMaterial(linewidth=5, vertexColors='VertexColors'), type='LinePieces', ) balls = [] for p in atoms.itertuples(): positions = (p.x * p.a, p.y * p.a, p.z * p.a) new_ball = Mesh( geometry=SphereGeometry(radius=p.sigma, widthSegments=32, heightSegments=24), material=MeshLambertMaterial(color=rgb2hex(c(p.epsilon_norm))), position=positions) balls.append(new_ball) # [scale*2*a,scale*2*a,scale*2*a] camera = PerspectiveCamera(position=[25, a, a], up=[0, 1, 0], children=[ DirectionalLight(color='white', position=[3, 5, 1], intensity=0.5) ]) scene = Scene(children=[ scale_lines, lines, *balls, camera, AmbientLight(color='#777') ]) renderer = Renderer( camera=camera, scene=scene, controls=[OrbitControls(controlling=camera, target=[a, a, a])]) return renderer
def _get_cylinder_from_vec(v0, v1, radius=0.15, color="#FFFFFF"): v0 = np.array(v0) v1 = np.array(v1) vec = v1 - v0 mid_point = (v0 + v1) / 2. rot_vec = np.cross([0, 1, 0], vec) rot_vec_len = np.linalg.norm(rot_vec) rot_vec = rot_vec / rot_vec_len rot_arg = np.arccos(np.dot([0, 1, 0], vec) / np.linalg.norm(vec)) new_bond = Mesh(geometry=CylinderBufferGeometry(radiusTop=radius, radiusBottom=radius, height=np.linalg.norm(v1 - v0), radialSegments=12, heightSegments=10), material=MeshLambertMaterial(color=color), position=tuple(mid_point)) rot = R.from_rotvec(rot_arg * rot_vec) new_bond.quaternion = tuple(rot.as_quat()) return new_bond
def _get_spheres(ctk_scene, d_args=None): if ctk_scene.phiEnd and ctk_scene.phiStart: phi_length = ctk_scene.phiEnd - ctk_scene.phiStart else: phi_length = np.pi * 2 return [ Mesh( geometry=SphereBufferGeometry( radius=ctk_scene.radius, phiStart=ctk_scene.phiStart or 0, phiLength=phi_length, widthSegments=32, heightSegments=32, ), material=MeshLambertMaterial(color=ctk_scene.color), position=tuple(ipos), ) for ipos in ctk_scene.positions ]
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 _get_cylinder_from_vec(v0, v1, d_args=None): """Draw the cylinder given the two endpoints. Args: v0 (list): one endpoint of line v1 (list): other endpoint of line d_args (dict): properties of the line (line_width and color) Returns: Mesh: Pythreejs object that displays the cylinders """ obj_args = update_object_args(d_args, "Cylinders", ['radius', 'color']) v0 = np.array(v0) v1 = np.array(v1) vec = v1 - v0 mid_point = (v0 + v1) / 2.0 rot_vec = np.cross([0, 1, 0], vec) rot_vec_len = np.linalg.norm(rot_vec) rot_vec = rot_vec / rot_vec_len rot_arg = np.arccos(np.dot([0, 1, 0], vec) / np.linalg.norm(vec)) new_bond = Mesh( geometry=CylinderBufferGeometry( radiusTop=obj_args['radius'], radiusBottom=obj_args['radius'], height=np.linalg.norm(v1 - v0), radialSegments=12, heightSegments=10, ), material=MeshLambertMaterial(color=obj_args['color']), position=tuple(mid_point), ) rot = R.from_rotvec(rot_arg * rot_vec) quat = tuple(rot.as_quat()) if any(isnan(itr_q) for itr_q in quat): new_bond.quaternion = (0, 0, 0, 0) else: new_bond.quaternion = quat return new_bond
def __init__(self, color='red', radius=0.025): Mesh.__init__(self, geometry=SphereGeometry(radius=radius), material=MeshLambertMaterial(color=color))
""" Link up the StructureMoleculeComponent objects to pythreejs Also includes some helper functions for draw addition objects using pythreejs """ from pythreejs import MeshLambertMaterial, Mesh, SphereBufferGeometry, CylinderBufferGeometry, Object3D, LineSegments2, LineSegmentsGeometry, LineMaterial, Scene, AmbientLight, Renderer, OrbitControls, OrthographicCamera, DirectionalLight from crystal_toolkit.components.structure import StructureMoleculeComponent from IPython.display import display from scipy.spatial.transform import Rotation as R import numpy as np ball = Mesh(geometry=SphereBufferGeometry(radius=1, widthSegments=32, heightSegments=16), material=MeshLambertMaterial(color='red'), position=[0, 1, 0]) def traverse_scene_object(scene_data, parent=None): """ Recursivesly populate a scene object with tree of children :param scene_data: :param parent: :return: """ for sub_object in scene_data["contents"]: if "type" in sub_object.keys(): parent.add(convert_object_to_pythreejs(sub_object)) else: new_parent = Object3D(name=sub_object["name"]) if parent is None: parent = new_parent
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)