diff --git a/CHANGELOG.md b/CHANGELOG.md index d259468bf0..9a6e896c60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added KLayout plugin, with DRC functionality for running design rule checks in `plugins.klayout.drc`. Supports running DRC on GDS files as well as `Geometry`, `Structure`, and `Simulation` objects. - Added "mil" and "in" (inch) units to `plot_length_units`. - Objective functions that involve running `tidy3d.plugins.smatrix.ComponentModeler` can be differentiated with autograd. +- `Scene.plot_3d()` method to make 3D rendering of scene. ### Changed - Validate mode solver object for large number of grid points on the modal plane. diff --git a/tidy3d/components/scene.py b/tidy3d/components/scene.py index 83abbcf330..23c536eb00 100644 --- a/tidy3d/components/scene.py +++ b/tidy3d/components/scene.py @@ -69,6 +69,7 @@ equal_aspect, plot_params_fluid, plot_params_structure, + plot_scene_3d, polygon_path, ) @@ -1955,3 +1956,15 @@ def _pcolormesh_shape_doping_box( alpha=alpha, clip_box=ax.bbox, ) + + def plot_3d(self, width=800, height=800) -> None: + """Render 3D plot of ``Scene`` (in jupyter notebook only). + Parameters + ---------- + width : float = 800 + width of the 3d view dom's size + height : float = 800 + height of the 3d view dom's size + + """ + return plot_scene_3d(self, width=width, height=height) diff --git a/tidy3d/components/viz/__init__.py b/tidy3d/components/viz/__init__.py index 1f3b5fb2ca..8e0b57feba 100644 --- a/tidy3d/components/viz/__init__.py +++ b/tidy3d/components/viz/__init__.py @@ -21,7 +21,7 @@ plot_params_structure, plot_params_symmetry, ) -from .plot_sim_3d import plot_sim_3d +from .plot_sim_3d import plot_scene_3d, plot_sim_3d from .styles import ( ARROW_ALPHA, ARROW_COLOR_MONITOR, @@ -75,6 +75,7 @@ "plot_params_source", "plot_params_structure", "plot_params_symmetry", + "plot_scene_3d", "plot_sim_3d", "polygon_patch", "polygon_path", diff --git a/tidy3d/components/viz/plot_sim_3d.py b/tidy3d/components/viz/plot_sim_3d.py index 46cf50c256..75e51327af 100644 --- a/tidy3d/components/viz/plot_sim_3d.py +++ b/tidy3d/components/viz/plot_sim_3d.py @@ -5,8 +5,63 @@ from tidy3d.exceptions import SetupError -def plot_sim_3d(sim, width=800, height=800) -> None: - """Make 3D display of simulation in ipyython notebook.""" +def plot_scene_3d(scene, width=800, height=800) -> None: + import gzip + import json + from base64 import b64encode + from io import BytesIO + + import h5py + + # Serialize scene to HDF5 in-memory + buffer = BytesIO() + scene.to_hdf5(buffer) + buffer.seek(0) + + # Open source HDF5 for reading and prepare modified copy + with h5py.File(buffer, "r") as src: + buffer2 = BytesIO() + with h5py.File(buffer2, "w") as dst: + + def copy_item(name, obj): + if isinstance(obj, h5py.Group): + dst.create_group(name) + for k, v in obj.attrs.items(): + dst[name].attrs[k] = v + elif isinstance(obj, h5py.Dataset): + data = obj[()] + if name == "JSON_STRING": + # Parse and update JSON string + json_str = ( + data.decode("utf-8") if isinstance(data, (bytes, bytearray)) else data + ) + json_data = json.loads(json_str) + json_data["size"] = list(scene.size) + json_data["center"] = list(scene.center) + json_data["grid_spec"] = {} + new_str = json.dumps(json_data) + dst.create_dataset(name, data=new_str.encode("utf-8")) + else: + dst.create_dataset(name, data=data) + for k, v in obj.attrs.items(): + dst[name].attrs[k] = v + + src.visititems(copy_item) + buffer2.seek(0) + + # Gzip the modified HDF5 + gz_buffer = BytesIO() + with gzip.GzipFile(fileobj=gz_buffer, mode="wb") as gz: + gz.write(buffer2.read()) + gz_buffer.seek(0) + + # Base64 encode and display with gzipped flag + sim_base64 = b64encode(gz_buffer.read()).decode("utf-8") + plot_sim_3d(sim_base64, width=width, height=height, is_gz_base64=True) + + +def plot_sim_3d(sim, width=800, height=800, is_gz_base64=False) -> None: + """Make 3D display of simulation in ipython notebook.""" try: from IPython.display import HTML, display @@ -19,10 +74,14 @@ def plot_sim_3d(sim, width=800, height=800) -> None: from base64 import b64encode from io import BytesIO - buffer = BytesIO() - sim.to_hdf5_gz(buffer) - buffer.seek(0) - base64 = b64encode(buffer.read()).decode("utf-8") + if not is_gz_base64: + buffer = BytesIO() + sim.to_hdf5_gz(buffer) + buffer.seek(0) + base64 = b64encode(buffer.read()).decode("utf-8") + else: + base64 = sim + js_code = """ /** * Simulation Viewer Injector