diff --git a/backend/icons/MMI1x2.png b/backend/icons/MMI1x2.png new file mode 100644 index 0000000..47b8260 Binary files /dev/null and b/backend/icons/MMI1x2.png differ diff --git a/backend/icons/MMI2x4.png b/backend/icons/MMI2x4.png new file mode 100644 index 0000000..5b07929 Binary files /dev/null and b/backend/icons/MMI2x4.png differ diff --git a/backend/icons/MZIs.png b/backend/icons/MZIs.png new file mode 100644 index 0000000..ec4771c Binary files /dev/null and b/backend/icons/MZIs.png differ diff --git a/backend/icons/Mach_Zender_modulators.png b/backend/icons/Mach_Zender_modulators.png index 5ae486f..f47d7a5 100644 Binary files a/backend/icons/Mach_Zender_modulators.png and b/backend/icons/Mach_Zender_modulators.png differ diff --git a/backend/icons/Phase_shifters_PN.png b/backend/icons/Phase_shifters_PN.png new file mode 100644 index 0000000..e3f5eac Binary files /dev/null and b/backend/icons/Phase_shifters_PN.png differ diff --git a/backend/icons/bendings.png b/backend/icons/bendings.png index 61449c0..b898bc6 100644 Binary files a/backend/icons/bendings.png and b/backend/icons/bendings.png differ diff --git a/backend/icons/capacitors.png b/backend/icons/capacitors.png index 3d1b9fc..6184295 100644 Binary files a/backend/icons/capacitors.png and b/backend/icons/capacitors.png differ diff --git a/backend/icons/crossings.png b/backend/icons/crossings.png new file mode 100644 index 0000000..0667570 Binary files /dev/null and b/backend/icons/crossings.png differ diff --git a/backend/icons/directional_couplers.png b/backend/icons/directional_couplers.png index 2e7a213..1753cb7 100644 Binary files a/backend/icons/directional_couplers.png and b/backend/icons/directional_couplers.png differ diff --git a/backend/icons/edge_couplers.png b/backend/icons/edge_couplers.png index 870700b..e12f628 100644 Binary files a/backend/icons/edge_couplers.png and b/backend/icons/edge_couplers.png differ diff --git a/backend/icons/grating_couplers.png b/backend/icons/grating_couplers.png index 520247f..989f065 100644 Binary files a/backend/icons/grating_couplers.png and b/backend/icons/grating_couplers.png differ diff --git a/backend/icons/multimode_interferometers.png b/backend/icons/multimode_interferometers.png index 1d0b568..a0e6ab8 100644 Binary files a/backend/icons/multimode_interferometers.png and b/backend/icons/multimode_interferometers.png differ diff --git a/backend/icons/phase_shifters.png b/backend/icons/phase_shifters.png index 96d5b63..d84fbf5 100644 Binary files a/backend/icons/phase_shifters.png and b/backend/icons/phase_shifters.png differ diff --git a/backend/icons/photodetectors.png b/backend/icons/photodetectors.png index 52c46ec..4823e67 100644 Binary files a/backend/icons/photodetectors.png and b/backend/icons/photodetectors.png differ diff --git a/backend/icons/resistors.png b/backend/icons/resistors.png index 8925935..0e58422 100644 Binary files a/backend/icons/resistors.png and b/backend/icons/resistors.png differ diff --git a/backend/icons/terminations.png b/backend/icons/terminations.png index 0dd8c0a..bb10fea 100644 Binary files a/backend/icons/terminations.png and b/backend/icons/terminations.png differ diff --git a/backend/icons/transitions.png b/backend/icons/transitions.png new file mode 100644 index 0000000..060bbe9 Binary files /dev/null and b/backend/icons/transitions.png differ diff --git a/backend/server.py b/backend/server.py index 7991129..bcde967 100644 --- a/backend/server.py +++ b/backend/server.py @@ -913,10 +913,18 @@ def getLib(): @app.route('/api/component/') @login_required_json def getComp(component_name): - """Return component YAML data.""" - data = readCompYaml(component_name, pdk_root_for_request_project()) + """Return component YAML data with injected category.""" + comps_root = pdk_root_for_request_project() + data = readCompYaml(component_name, comps_root) if data is None: return jsonify({"error": "Component not found"}), 404 + # Derive the category (parent folder name) for icon resolution on the canvas. + if isinstance(data, dict) and '__category__' not in data: + search_root = comps_root or current_pdk_root() + for root, dirs, files in os.walk(search_root): + if os.path.basename(root) == component_name: + data['__category__'] = os.path.basename(os.path.dirname(root)) + break return jsonify(data) @app.route('/api/component//image') diff --git a/frontend/canvas-helpers.js b/frontend/canvas-helpers.js index 76324f4..31d35ed 100644 --- a/frontend/canvas-helpers.js +++ b/frontend/canvas-helpers.js @@ -12,6 +12,13 @@ } root.MxpicCanvasHelpers = helpers; })(typeof window !== 'undefined' ? window : globalThis, function () { + // Global origin for YAML coordinate export/import so YAML values always match + // the RightPanel display coordinate system. + let __exportOriginX = null; + let __exportOriginY = null; + var setExportOrigin = (x, y) => { __exportOriginX = x; __exportOriginY = y; }; + var getExportOrigin = () => ({ x: __exportOriginX, y: __exportOriginY }); + // Label used by the canvas to represent generated mxpic_forge components. const FORGE_COMPONENT_LABEL = 'generate with mxpic_forge'; // Serialized component type used when saving mxpic_forge-generated components. @@ -651,10 +658,20 @@ return JSON.stringify(String(value)); }; - // Convert canvas Y coordinates into layout Y coordinates. - const canvasToLayoutY = (value) => -Number(value || 0); - // Convert layout Y coordinates back into canvas Y coordinates. - const layoutToCanvasY = (value) => -Number(value || 0); + // Convert canvas coordinates into layout/display coordinates. + // When origin is set, uses the RightPanel formula: displayY = originY - rfY + // Falls back to simple negation for backward compatibility. + const canvasToLayoutY = (value) => { const v = Number(value || 0); return __exportOriginY != null ? __exportOriginY - v : -v; }; + const canvasToLayoutX = (value) => { const v = Number(value || 0); return __exportOriginX != null ? v - __exportOriginX : v; }; + // Convert layout/display coordinates back into canvas coordinates. + // Uses self-inverse formula for Y: rfY = originY - displayY (same as above). + // Import: layout/display coords → canvas coords. Optional originY/originX allow + // per-file overrides (e.g. from a YAML canvas_origin field). When omitted the + // module-level origin is used; null origin falls back to simple negation / pass-through. + const layoutToCanvasY = (value, originY) => { const v = Number(value || 0); const oy = originY !== undefined ? originY : __exportOriginY; return oy != null ? oy - v : -v; }; + const layoutToCanvasX = (value, originX) => { const v = Number(value || 0); const ox = originX !== undefined ? originX : __exportOriginX; return ox != null ? v + ox : v; }; + + // Serialize nested component settings into YAML blocks. const buildSettingsYaml = (settings, indent) => { @@ -677,8 +694,8 @@ : '\n settings:\n length:'; return ` ${instanceName}: - component: ${componentValue} - x: ${Number(position.x || 0).toFixed(1)} + component: "${componentValue}" + x: ${canvasToLayoutX(position.x).toFixed(1)} y: ${canvasToLayoutY(position.y).toFixed(1)} rotation: ${Number(rotation || 0).toFixed(1)} flip: ${flip ? 1 : 0} @@ -908,7 +925,7 @@ // Convert standalone port nodes into page-level layout pins. const buildPageComponentPins = (port, nodes) => { - const portNodes = (nodes || []).filter(isPortElementNode); + const portNodes = (nodes || []).filter(n => n.id === 'page-port'); if (portNodes.length > 0) { return portNodes.reduce((pins, node) => { const data = node.data || {}; @@ -962,7 +979,7 @@ ${data.layer ? `layer: ${data.layer}` : 'layer: WG_CORE'} element: ${info.element} pin: ${info.pin} - x: ${Number(info.x || 0).toFixed(1)} + x: ${canvasToLayoutX(info.x).toFixed(1)} y: ${canvasToLayoutY(info.y).toFixed(1)} angle: ${Number(info.a || 0).toFixed(1)} width: ${Number(info.width || 0.5)}${description}`; @@ -977,7 +994,7 @@ // Serialize built-in port and anchor nodes into layout element metadata. const buildElementsYaml = (nodes) => { - const elementNodes = (nodes || []).filter(isElementNode); + const elementNodes = (nodes || []).filter(n => isElementNode(n) && n.id !== 'page-port'); if (elementNodes.length === 0) return 'elements: {}'; const lines = elementNodes.map(node => { const data = node.data || {}; @@ -990,7 +1007,7 @@ .join('\n'); return ` ${name}: type: ${data.elementType} - x: ${Number((node.position && node.position.x) || 0).toFixed(1)} + x: ${canvasToLayoutX((node.position && node.position.x) || 0).toFixed(1)} y: ${canvasToLayoutY((node.position && node.position.y) || 0).toFixed(1)} angle: ${Number(angle || 0).toFixed(1)} pin_number: ${portNumber} @@ -1055,7 +1072,7 @@ ${pinLines}`; const storedPoints = Array.isArray(edge.data && edge.data.points) ? edge.data.points : []; const points = storedPoints.length >= 2 ? getEdgeRoutePoints(edge, nodeMap) : []; const pointsYaml = points.length > 0 - ? `\n points:\n${points.map(point => ` - x: ${Number(point.x || 0).toFixed(1)}\n y: ${canvasToLayoutY(point.y).toFixed(1)}`).join('\n')}` + ? `\n points:\n${points.map(point => ` - x: ${canvasToLayoutX(point.x).toFixed(1)}\n y: ${canvasToLayoutY(point.y).toFixed(1)}`).join('\n')}` : ''; const isFreeRoute = Boolean(edge.data && edge.data.freeRoute) || (!sourceNode && !targetNode && points.length >= 2); const linkYaml = isFreeRoute @@ -1311,8 +1328,12 @@ bundles:${groupsYaml ? `\n${groupsYaml}` : ' {}'}`; DEFAULT_FORGE_ARGUMENTS, FALLBACK_TECHNOLOGY_MANIFEST, FREE_WIRES_BUNDLE_GROUP, + setExportOrigin, + getExportOrigin, canvasToLayoutY, + canvasToLayoutX, layoutToCanvasY, + layoutToCanvasX, createForgeArguments, createRouteSettings, updateRouteField, diff --git a/frontend/canvas.html b/frontend/canvas.html index 12fa44a..5e8c4b3 100644 --- a/frontend/canvas.html +++ b/frontend/canvas.html @@ -1,5 +1,6 @@ +