Routing nchor added

This commit is contained in:
2026-05-29 21:51:57 +08:00
parent 1215bf978a
commit 07ee7f9dd7
22 changed files with 3230 additions and 426 deletions
+93 -27
View File
@@ -122,6 +122,36 @@ def cell_svg_path(project_name, cell_name):
return os.path.join(project_root(project_name), f"{safe_name(cell_name, 'canvas_1')}.svg")
def cell_routes_path(project_name, cell_name):
return os.path.join(project_root(project_name), f"{safe_name(cell_name, 'canvas_1')}.routes.yml")
def write_route_points_sidecar(yaml_content, output_path):
layout = yaml.safe_load(yaml_content) or {}
routes = {}
for bundle_name, bundle in (layout.get("bundles") or {}).items():
saved_links = []
for link in bundle.get("links") or []:
points = link.get("points") or []
if not points:
continue
saved_links.append({
"from": link.get("from"),
"to": link.get("to"),
"xsection": link.get("xsection"),
"width": link.get("width"),
"radius": link.get("radius"),
"points": points,
})
if saved_links:
routes[bundle_name] = {"links": saved_links}
if routes:
with open(output_path, 'w', encoding='utf-8') as file:
yaml.safe_dump({"routes": routes}, file, sort_keys=False)
elif os.path.exists(output_path):
os.remove(output_path)
def project_gds_path(project_name):
return os.path.join(project_root(project_name), f"{safe_name(project_name, 'project_1')}.gds")
@@ -145,6 +175,28 @@ def current_pdk_registry():
return PdkRegistry(current_pdk_root(), prefer_full_gds=prefer_full_gds_for_session(session))
def scoped_pdk_root_for_project(project_name):
base_root = current_pdk_root()
project = safe_name(project_name, '')
if not project:
return base_root
technology_id = read_project_meta(project).get("technology") or ""
if "/" not in technology_id:
return base_root
foundry, technology = technology_id.split("/", 1)
scoped_root = os.path.abspath(os.path.join(base_root, foundry, technology))
if scoped_root == base_root or not scoped_root.startswith(base_root + os.sep):
return base_root
return scoped_root if os.path.isdir(scoped_root) else base_root
def pdk_root_for_request_project():
project = request.args.get('project')
return scoped_pdk_root_for_project(project) if project else current_pdk_root()
def project_meta_path(project_name):
return os.path.join(project_root(project_name), ".project.json")
@@ -172,10 +224,11 @@ def ensure_project_path(project_name):
# ... [Keep countSpaces and buildTree exactly as they are] ...
def findComps(baseDir):
def findComps(baseDir, path_root=None):
"""Scan component folders, return map of paths -> component info."""
compMap = {}
refDir = baseDir
path_root = os.path.abspath(path_root or baseDir)
for root, dirs, files in os.walk(baseDir):
ymlFiles = [f for f in files if f.endswith('.yml')]
if ymlFiles:
@@ -191,7 +244,8 @@ def findComps(baseDir):
compMap[parts + (compName,)] = {
'folder': compName,
'yml': ymlFiles[0],
'category': category # Save the category to the map
'category': category, # Save the category to the map
'path': os.path.relpath(root, path_root).replace(os.sep, '/')
}
dirs.clear()
return compMap
@@ -214,7 +268,8 @@ def addCompsToTree(compMap):
"__type__": "component",
"__name__": compName,
"__yml__": compItem['yml'],
"__category__": compItem['category'] # Inject category into the tree
"__category__": compItem['category'], # Inject category into the tree
"__path__": compItem.get('path')
})
return fresh_tree
@@ -258,6 +313,15 @@ def readCompYaml(compName, comps_root=None):
return yaml.safe_load(f)
return None
def find_component_dir(component_name, comps_root=None):
search_root = comps_root or current_pdk_root()
for root, dirs, files in os.walk(search_root):
if os.path.basename(root) == component_name:
dirs.clear()
return root, files
return None, []
# --- AUTHENTICATION & PAGE ROUTES ---
@app.route('/')
def home():
@@ -588,25 +652,29 @@ def save_layout():
project = safe_name(data.get('project'), 'project_1')
cell = safe_name(data.get('cell'), 'canvas_1')
content = data.get('content', '')
create_preview = bool(data.get('preview', True))
save_path = cell_file_path(project, cell)
os.makedirs(os.path.dirname(save_path), exist_ok=True)
with open(save_path, 'w', encoding='utf-8') as f:
f.write(content)
write_route_points_sidecar(content, cell_routes_path(project, cell))
svg_path = cell_svg_path(project, cell)
if layout_has_links(content):
create_routed_layout_svg(
content,
svg_path,
pdk_root=current_pdk_root(),
project_dir=project_root(project),
technology_manifest_path=technology_manifest_path_for_project(project),
prefer_full_gds=prefer_full_gds_for_session(session),
)
else:
create_layout_svg_from_gds(content, svg_path, pdk_registry=current_pdk_registry(), project_dir=project_root(project))
svg_path = None
if create_preview:
svg_path = cell_svg_path(project, cell)
if layout_has_links(content):
create_routed_layout_svg(
content,
svg_path,
pdk_root=current_pdk_root(),
project_dir=project_root(project),
technology_manifest_path=technology_manifest_path_for_project(project),
prefer_full_gds=prefer_full_gds_for_session(session),
)
else:
create_layout_svg_from_gds(content, svg_path, pdk_registry=current_pdk_registry(), project_dir=project_root(project))
record_action('layout.save', project=project, cell=cell, detail={"bytes": len(content), "svg": svg_path})
return jsonify({
@@ -615,7 +683,7 @@ def save_layout():
"cell": cell,
"path": save_path,
"svg_path": svg_path,
"svg_url": url_for('get_layout_svg', project_name=project, cell_name=cell)
"svg_url": url_for('get_layout_svg', project_name=project, cell_name=cell) if svg_path else None
}), 200
except Exception as e:
@@ -716,10 +784,10 @@ def get_project_gds(project_name, filename):
@login_required_json
def getLib():
"""Get library structure."""
comps_root = current_pdk_root()
comps_root = pdk_root_for_request_project()
fresh_tree = {}
if os.path.isdir(comps_root):
compMap = findComps(comps_root)
compMap = findComps(comps_root, current_pdk_root())
fresh_tree = addCompsToTree(compMap)
return jsonify(fresh_tree)
@@ -729,7 +797,7 @@ def getLib():
@login_required_json
def getComp(component_name):
"""Return component YAML data."""
data = readCompYaml(component_name, current_pdk_root())
data = readCompYaml(component_name, pdk_root_for_request_project())
if data is None:
return jsonify({"error": "Component not found"}), 404
return jsonify(data)
@@ -738,14 +806,12 @@ def getComp(component_name):
@login_required_json
def getCompImg(component_name):
"""Return first image in component folder."""
for root, dirs, files in os.walk(current_pdk_root()):
if os.path.basename(root) == component_name:
dirs.clear()
for ext in ('.png', '.jpg', '.jpeg', '.svg'):
for f in files:
if f.lower().endswith(ext):
return send_from_directory(root, f)
break
root, files = find_component_dir(component_name, pdk_root_for_request_project())
if root:
for ext in ('.png', '.jpg', '.jpeg', '.svg'):
for f in files:
if f.lower().endswith(ext):
return send_from_directory(root, f)
return jsonify({"error": "No image found"}), 404
if __name__ == '__main__':