More annotation added to the program
This commit is contained in:
@@ -13,6 +13,8 @@ import yaml
|
||||
def create_layout_svg_from_gds(yaml_content: str, output_path: str, pdk_registry, project_dir: str = None) -> str:
|
||||
"""Create an SVG preview by placing real public _BB.gds cells from layout YAML."""
|
||||
layout = yaml.safe_load(yaml_content) or {}
|
||||
# Try gdstk first because it can write SVG directly; keep Nazca as a GDS
|
||||
# placement fallback for environments where gdstk is unavailable.
|
||||
try:
|
||||
return _create_with_gdstk(layout, output_path, pdk_registry, project_dir)
|
||||
except ImportError as gdstk_error:
|
||||
@@ -26,6 +28,7 @@ def create_layout_svg_from_gds(yaml_content: str, output_path: str, pdk_registry
|
||||
|
||||
|
||||
def _create_with_gdstk(layout: dict, output_path: str, pdk_registry, project_dir: Optional[str]) -> str:
|
||||
"""Generate preview SVG geometry using gdstk import and placement APIs."""
|
||||
import gdstk
|
||||
|
||||
library = gdstk.Library()
|
||||
@@ -37,9 +40,12 @@ def _create_with_gdstk(layout: dict, output_path: str, pdk_registry, project_dir
|
||||
|
||||
|
||||
def _build_gdstk_cell(gdstk, library, layout: dict, pdk_registry, project_dir: Optional[str], cell_cache: Dict):
|
||||
"""Build or reuse a gdstk cell for a saved layout document."""
|
||||
cell_name = _safe_cell_name(layout.get("name") or "layout", library)
|
||||
top = library.new_cell(cell_name)
|
||||
|
||||
# Each saved instance becomes a GDS reference to either another project cell
|
||||
# or a resolved PDK asset.
|
||||
for instance_name, instance in (layout.get("instances") or {}).items():
|
||||
component = str(instance.get("component") or "")
|
||||
x = _number(instance.get("x"))
|
||||
@@ -54,9 +60,12 @@ def _build_gdstk_cell(gdstk, library, layout: dict, pdk_registry, project_dir: O
|
||||
|
||||
|
||||
def _resolve_child_cell(gdstk, library, component: str, pdk_registry, project_dir: Optional[str], cell_cache: Dict):
|
||||
"""Resolve a placed child component from local cells or PDK assets."""
|
||||
if component in cell_cache:
|
||||
return cell_cache[component]
|
||||
|
||||
# Project-local composite cells are resolved before external PDK components
|
||||
# so nested user-created cells can appear in preview output.
|
||||
local_layout = _load_local_layout(component, project_dir)
|
||||
if local_layout is not None:
|
||||
child = _build_gdstk_cell(gdstk, library, local_layout, pdk_registry, project_dir, cell_cache)
|
||||
@@ -72,10 +81,12 @@ def _resolve_child_cell(gdstk, library, component: str, pdk_registry, project_di
|
||||
|
||||
|
||||
def _import_gds_cell(gdstk, library, gds_path: str):
|
||||
"""Import a GDS file and return the first usable cell for placement."""
|
||||
source = gdstk.read_gds(gds_path)
|
||||
top_cells = source.top_level()
|
||||
if not top_cells:
|
||||
raise ValueError(f"No top-level cell found in {gds_path}")
|
||||
# Reuse already-imported cells by name to keep the preview library compact.
|
||||
for source_cell in source.cells:
|
||||
if _library_cell_by_name(library, source_cell.name) is None:
|
||||
library.add(source_cell)
|
||||
@@ -83,9 +94,12 @@ def _import_gds_cell(gdstk, library, gds_path: str):
|
||||
|
||||
|
||||
def _create_with_nazca(layout: dict, output_path: str, pdk_registry, project_dir: Optional[str]) -> str:
|
||||
"""Generate preview SVG geometry using Nazca when gdstk is unavailable."""
|
||||
import nazca as nd
|
||||
|
||||
png_path = os.path.splitext(output_path)[0] + ".gds"
|
||||
# Nazca can place the same GDS references as the preview path, but this
|
||||
# backend still requires gdstk for final SVG conversion.
|
||||
with nd.Cell(str(layout.get("name") or "layout")) as top:
|
||||
for instance_name, instance in (layout.get("instances") or {}).items():
|
||||
component = str(instance.get("component") or "")
|
||||
@@ -101,6 +115,7 @@ def _create_with_nazca(layout: dict, output_path: str, pdk_registry, project_dir
|
||||
|
||||
|
||||
def _load_local_layout(component: str, project_dir: Optional[str]) -> Optional[dict]:
|
||||
"""Load a project-local composite layout referenced by another cell."""
|
||||
if not project_dir or "/" in component or "\\" in component or component == "generate_with_forge":
|
||||
return None
|
||||
for ext in (".yml", ".yaml"):
|
||||
@@ -112,6 +127,7 @@ def _load_local_layout(component: str, project_dir: Optional[str]) -> Optional[d
|
||||
|
||||
|
||||
def _safe_cell_name(name, library) -> str:
|
||||
"""Generate a backend-safe unique cell name for GDS/Nazca libraries."""
|
||||
base = "".join(ch if ch.isalnum() or ch in "._$" else "_" for ch in str(name)) or "layout"
|
||||
candidate = base
|
||||
counter = 1
|
||||
@@ -122,6 +138,7 @@ def _safe_cell_name(name, library) -> str:
|
||||
|
||||
|
||||
def _library_cell_by_name(library, name: str):
|
||||
"""Find a cell object in a loaded layout library by name."""
|
||||
for cell in library.cells:
|
||||
if cell.name == name:
|
||||
return cell
|
||||
@@ -129,6 +146,7 @@ def _library_cell_by_name(library, name: str):
|
||||
|
||||
|
||||
def _number(value, default=0.0) -> float:
|
||||
"""Convert numeric YAML values to floats with a stable default."""
|
||||
try:
|
||||
if value is None or value == "":
|
||||
return default
|
||||
|
||||
Reference in New Issue
Block a user