def OldStarMesh(temp=Te_Sun, rad=1, scale=(1, 1, 1), pos=[0, 0, 0]): """ This function creates a pythreejs object that represents a star using a texture based on public domain STEREO Heliographic map made with images taken of the Sun on Dec. 30, 2011. Image downloaded from https://stereo.gsfc.nasa.gov/360blog/ and had its brightness and resolution rescaled. Parameters ---------- temp : float temperature of star in Kelvin (default 5777) rad : float radius of the star in system units (default 1) scale : tuple pythreejs scale in each dimension as tuple (default (1, 1, 1) ) pos : list three-dimensional position as list (default [0, 0, 0] ) Returns ------- star : pythreejs.Mesh a spherical pythreejs Mesh object representing a star """ # Check is position is a list if isinstance(pos, list): # Check if this is a list of 3 items if (len(pos) != 3): raise TypeError('pos passed to StarMesh must be list of 3 numbers') # Check that all the items in the list are numbers for this_pos in pos: try: i = float(this_pos) except ValueError: raise TypeError('ValueError: pos contains list item that is not a number.') else: raise TypeError('pos passed to StarMesh must be list of 3 numbers') # Check is scale is a tuple if isinstance(scale, tuple): if (len(scale) != 3): raise TypeError('scale passed to StarMesh must be tuple of 3 numbers') else: raise TypeError('scale must be a tuple') # Define color and texture of star surface hexcolor = tc.rgb2hex(tc.temp2rgb(float(temp)))[0] StarTexture = p3j.ImageTexture(imageUri='images/sun_surface.jpg') # Create sphere using MeshBasicMaterial (which is unaffected by lighting) StarSurface = p3j.MeshBasicMaterial(color=hexcolor, map=StarTexture) StarGeom = p3j.SphereBufferGeometry(radius=rad, widthSegments=32, heightSegments=16) return p3j.Mesh(geometry=StarGeom, material=StarSurface, position=pos, scale=scale)
def test_sphere(): sphere = Sphere(10.0, name='sphere', color='azure') assert sphere.name == 'sphere' assert sphere.__str__() == \ 'Sphere sphere color:azure material:default radius:10.0' assert sphere.__repr__() == 'Sphere' assert sphere.radius == 10.0 assert sphere.color == 'azure' if p3js is not None: mesh = sphere._p3js_mesh() expected_mesh = p3js.Mesh(p3js.SphereBufferGeometry(radius=10.0), p3js.MeshStandardMaterial(color='azure'), name='sphere') assert repr(mesh) == repr(expected_mesh) sphere.name = 'sphere1' assert sphere.name == 'sphere1' sphere.radius = 14.0 assert sphere.radius == 14.0 sphere.color = 'aqua' assert sphere.color == 'aqua' assert sphere.generate_dict() == { "color": "aqua", "type": "Sphere", "name": "sphere1", "radius": 14.0, "material": "default" } assert isinstance(sphere, Shape) sphere_ = Sphere(10.0, color='azure') assert sphere_.name == 'unnamed' assert sphere_.__str__() == \ 'Sphere unnamed color:azure material:default radius:10.0' assert sphere_.__repr__() == 'Sphere'
def StarMesh(temp=Te_Sun, rad=1, scale=(1, 1, 1), pos=[0, 0, 0]): """ This function creates a pythreejs object that represents a star using a set of nested spheres that are partly transparent to simulate limb darkening. Parameters ---------- temp : float temperature of star in Kelvin (default 5777) rad : float radius of the star in system units (default 1) scale : tuple pythreejs scale in each dimension as tuple (default (1, 1, 1) ) pos : list three-dimensional position as list (default [0, 0, 0] ) Returns ------- star : pythreejs.Mesh a spherical pythreejs Mesh object representing a star """ # Check is position is a list if isinstance(pos, list): # Check if this is a list of 3 items if (len(pos) != 3): raise TypeError('pos passed to StarMesh must be list of 3 numbers') # Check that all the items in the list are numbers for this_pos in pos: try: i = float(this_pos) except ValueError: raise TypeError( 'ValueError: pos contains list item that is not a number.') else: raise TypeError('pos passed to StarMesh must be list of 3 numbers') # Check is scale is a tuple if isinstance(scale, tuple): if (len(scale) != 3): raise TypeError( 'scale passed to StarMesh must be tuple of 3 numbers') else: raise TypeError('scale must be a tuple') # Define color of star surface hexcolor = tc.rgb2hex(tc.temp2rgb(float(temp)))[0] # Number of transparent layers to use to build star layers = 20 # Radial scaling drad = 0.97 # Starting resolution N_azi, N_pol = 32, 16 # Layer opacity tau = 0.4 # Radii to use radii = rad * drad**np.array(range(layers - 1, -1, -1)) # 3D object to represent star star = p3j.Object3D() # Build the object from inside out for i in range(layers): # Tweak number of vertices in sphere up for the outer surface sphere if (i > (layers - 2)): N_azi *= 2 N_pol *= 2 geom = p3j.SphereBufferGeometry(radius=radii[i], widthSegments=N_azi, heightSegments=N_pol, renderOrder=-i) material = p3j.MeshBasicMaterial(color=hexcolor, transparent=True, opacity=tau) star.add(p3j.Mesh(geom, material)) # Set the position and scale of this mesh star.position = pos star.scale = scale return star
def draw_sphere(sphere, color=None, segments=32): geo = p3js.SphereBufferGeometry(sphere.radius, segments, segments) mat = material_from_color(color) mesh = p3js.Mesh(geometry=geo, material=mat) mesh.position = list(sphere.point) return mesh
def generate_3js_render( element_groups, canvas_size, zoom, camera_fov=30, background_color="white", background_opacity=1.0, reuse_objects=False, use_atom_arrays=False, use_label_arrays=False, ): """Create a pythreejs scene of the elements. Regarding initialisation performance, see: https://github.com/jupyter-widgets/pythreejs/issues/154 """ import pythreejs as pjs key_elements = {} group_elements = pjs.Group() key_elements["group_elements"] = group_elements unique_atom_sets = {} for el in element_groups["atoms"]: element_hash = ( ("radius", el.sradius), ("color", el.color), ("fill_opacity", el.fill_opacity), ("stroke_color", el.get("stroke_color", "black")), ("ghost", el.ghost), ) unique_atom_sets.setdefault(element_hash, []).append(el) group_atoms = pjs.Group() group_ghosts = pjs.Group() atom_geometries = {} atom_materials = {} outline_materials = {} for el_hash, els in unique_atom_sets.items(): el = els[0] data = dict(el_hash) if reuse_objects: atom_geometry = atom_geometries.setdefault( el.sradius, pjs.SphereBufferGeometry(radius=el.sradius, widthSegments=30, heightSegments=30), ) else: atom_geometry = pjs.SphereBufferGeometry(radius=el.sradius, widthSegments=30, heightSegments=30) if reuse_objects: atom_material = atom_materials.setdefault( (el.color, el.fill_opacity), pjs.MeshLambertMaterial(color=el.color, transparent=True, opacity=el.fill_opacity), ) else: atom_material = pjs.MeshLambertMaterial(color=el.color, transparent=True, opacity=el.fill_opacity) if use_atom_arrays: atom_mesh = pjs.Mesh(geometry=atom_geometry, material=atom_material) atom_array = pjs.CloneArray( original=atom_mesh, positions=[e.position.tolist() for e in els], merge=False, ) else: atom_array = [ pjs.Mesh( geometry=atom_geometry, material=atom_material, position=e.position.tolist(), name=e.info_string, ) for e in els ] data["geometry"] = atom_geometry data["material_body"] = atom_material if el.ghost: key_elements["group_ghosts"] = group_ghosts group_ghosts.add(atom_array) else: key_elements["group_atoms"] = group_atoms group_atoms.add(atom_array) if el.get("stroke_width", 1) > 0: if reuse_objects: outline_material = outline_materials.setdefault( el.get("stroke_color", "black"), pjs.MeshBasicMaterial( color=el.get("stroke_color", "black"), side="BackSide", transparent=True, opacity=el.get("stroke_opacity", 1.0), ), ) else: outline_material = pjs.MeshBasicMaterial( color=el.get("stroke_color", "black"), side="BackSide", transparent=True, opacity=el.get("stroke_opacity", 1.0), ) # TODO use stroke width to dictate scale if use_atom_arrays: outline_mesh = pjs.Mesh( geometry=atom_geometry, material=outline_material, scale=(1.05, 1.05, 1.05), ) outline_array = pjs.CloneArray( original=outline_mesh, positions=[e.position.tolist() for e in els], merge=False, ) else: outline_array = [ pjs.Mesh( geometry=atom_geometry, material=outline_material, position=e.position.tolist(), scale=(1.05, 1.05, 1.05), ) for e in els ] data["material_outline"] = outline_material if el.ghost: group_ghosts.add(outline_array) else: group_atoms.add(outline_array) key_elements.setdefault("atom_arrays", []).append(data) group_elements.add(group_atoms) group_elements.add(group_ghosts) group_labels = add_labels(element_groups, key_elements, use_label_arrays) group_elements.add(group_labels) if len(element_groups["cell_lines"]) > 0: cell_line_mat = pjs.LineMaterial( linewidth=1, color=element_groups["cell_lines"].group_properties["color"]) cell_line_geo = pjs.LineSegmentsGeometry(positions=[ el.position.tolist() for el in element_groups["cell_lines"] ]) cell_lines = pjs.LineSegments2(geometry=cell_line_geo, material=cell_line_mat) key_elements["cell_lines"] = cell_lines group_elements.add(cell_lines) if len(element_groups["bond_lines"]) > 0: bond_line_mat = pjs.LineMaterial( linewidth=element_groups["bond_lines"]. group_properties["stroke_width"], vertexColors="VertexColors", ) bond_line_geo = pjs.LineSegmentsGeometry( positions=[ el.position.tolist() for el in element_groups["bond_lines"] ], colors=[[Color(c).rgb for c in el.color] for el in element_groups["bond_lines"]], ) bond_lines = pjs.LineSegments2(geometry=bond_line_geo, material=bond_line_mat) key_elements["bond_lines"] = bond_lines group_elements.add(bond_lines) group_millers = pjs.Group() if len(element_groups["miller_lines"]) or len( element_groups["miller_planes"]): key_elements["group_millers"] = group_millers if len(element_groups["miller_lines"]) > 0: miller_line_mat = pjs.LineMaterial( linewidth=3, vertexColors="VertexColors" # TODO use stroke_width ) miller_line_geo = pjs.LineSegmentsGeometry( positions=[ el.position.tolist() for el in element_groups["miller_lines"] ], colors=[[Color(el.stroke_color).rgb] * 2 for el in element_groups["miller_lines"]], ) miller_lines = pjs.LineSegments2(geometry=miller_line_geo, material=miller_line_mat) group_millers.add(miller_lines) for el in element_groups["miller_planes"]: vertices = el.position.tolist() faces = [( 0, 1, 2, triangle_normal(vertices[0], vertices[1], vertices[2]), "black", 0, )] if len(vertices) == 4: faces.append(( 2, 3, 0, triangle_normal(vertices[2], vertices[3], vertices[0]), "black", 0, )) elif len(vertices) != 3: raise NotImplementedError("polygons with more than 4 points") plane_geom = pjs.Geometry(vertices=vertices, faces=faces) plane_mat = pjs.MeshBasicMaterial( color=el.fill_color, transparent=True, opacity=el.fill_opacity, side="DoubleSide", ) plane_mesh = pjs.Mesh(geometry=plane_geom, material=plane_mat) group_millers.add(plane_mesh) group_elements.add(group_millers) scene = pjs.Scene(background=None) scene.add([group_elements]) view_width, view_height = canvas_size minp, maxp = element_groups.get_position_range() # compute a minimum camera distance, that is guaranteed to encapsulate all elements camera_dist = maxp[2] + sqrt(maxp[0]**2 + maxp[1]**2) / tan( radians(camera_fov / 2)) camera = pjs.PerspectiveCamera( fov=camera_fov, position=[0, 0, camera_dist], aspect=view_width / view_height, zoom=zoom, ) scene.add([camera]) ambient_light = pjs.AmbientLight(color="lightgray") key_elements["ambient_light"] = ambient_light direct_light = pjs.DirectionalLight(position=(maxp * 2).tolist()) key_elements["direct_light"] = direct_light scene.add([camera, ambient_light, direct_light]) camera_control = pjs.OrbitControls(controlling=camera, screenSpacePanning=True) atom_picker = pjs.Picker(controlling=group_atoms, event="dblclick") key_elements["atom_picker"] = atom_picker material = pjs.SpriteMaterial( map=create_arrow_texture(right=False), transparent=True, depthWrite=False, depthTest=False, ) atom_pointer = pjs.Sprite(material=material, scale=(4, 3, 1), visible=False) scene.add(atom_pointer) key_elements["atom_pointer"] = atom_pointer renderer = pjs.Renderer( camera=camera, scene=scene, controls=[camera_control, atom_picker], width=view_width, height=view_height, alpha=True, clearOpacity=background_opacity, clearColor=background_color, ) return renderer, key_elements