auto-renaming process changed to abbreviation names with catagoreis, like DC, MMI and PD...

This commit is contained in:
2026-05-29 23:42:10 +08:00
parent 07ee7f9dd7
commit 80d7514740
12 changed files with 162 additions and 292 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 214 KiB

@@ -1,117 +0,0 @@
# =============================================
# mxPIC Cell/Project Definition File
# =============================================
schema_version: "2.0.0"
kind: cell
coordinate_system: gds_y_up
canvas_size:
width: 5000
height: 5000
project: mxpic_project_1
name: mxpic_project_1
type: project
version: "1.0.0"
# 1. External Ports (How this cell connects to the outside world)
ports:
- name: port
layer: WG_CORE
x: 50.0
y: -150.0
angle: 0.0
width: 0.5
# 2. Instances (The sub-components dropped onto this canvas)
instances:
component_1:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
x: 100.0
y: -2290.0
rotation: 0.0
flip: 0
flop: 0
mirror: false
settings:
length:
component_4:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
x: 100.0
y: -1970.0
rotation: 0.0
flip: 0
flop: 0
mirror: false
settings:
length:
component_2:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
x: 100.0
y: -2560.0
rotation: 0.0
flip: 0
flop: 0
mirror: false
settings:
length:
elements:
port:
type: port
x: 50.0
y: -150.0
angle: 0.0
layer: WG_CORE
width: 0.5
description: ""
anchor_1:
type: anchor
x: 120.0
y: -2150.0
angle: 0.0
layer: WG_CORE
width: 0.5
description: ""
anchor_2:
type: anchor
x: 130.0
y: -2430.0
angle: 0.0
layer: WG_CORE
width: 0.5
description: ""
# 3. Bundles (Grouped links for multi-bus/parallel routing)
bundles:
output_bus:
routing_type: euler_bend
links:
- from: anchor_1:right
to: component_4:b2
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: anchor_1:left
to: component_1:a1
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: component_1:b2
to: anchor_2:right
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: anchor_2:left
to: component_2:a1
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
Binary file not shown.
+136 -14
View File
@@ -3646,6 +3646,10 @@
if (!activePageId) return;
const relevantChanges = changes.filter(change => change.id !== '__canvas-boundary__');
if (relevantChanges.length === 0) return;
const removedNodeIds = new Set(relevantChanges.filter(change => change.type === 'remove').map(change => change.id));
if (removedNodeIds.size > 0 && activePage) {
releaseComponentDisplayNames(activePage.nodes.filter(node => removedNodeIds.has(node.id)));
}
setPages(prev => prev.map(p => {
if (p.id !== activePageId) return p;
const newNodes = applyNodeChanges(relevantChanges, p.nodes).map(node => {
@@ -3667,7 +3671,7 @@
}
return { ...p, nodes: newNodes, port: newPort };
}));
}, [activePageId, activeCanvasSize]);
}, [activePageId, activePage, activeCanvasSize]);
const onEdgesChange = useCallback((changes) => {
if (!activePageId) return;
@@ -3812,6 +3816,7 @@
const selectedNodes = activePage.nodes.filter(n => n.selected);
if (selectedNodes.length > 0) {
setClipboard({ nodes: JSON.parse(JSON.stringify(selectedNodes)) });
releaseComponentDisplayNames(selectedNodes);
const selectedNodeIds = new Set(selectedNodes.map(n => n.id));
const newNodes = activePage.nodes.filter(n => !selectedNodeIds.has(n.id));
const newEdges = activePage.edges.filter(e => !selectedNodeIds.has(e.source) && !selectedNodeIds.has(e.target));
@@ -3822,7 +3827,13 @@
const handlePaste = useCallback(() => {
if (!activePage || clipboard.nodes.length === 0) return;
const newNodes = clipboard.nodes.map(node => {
const copiedName = generateComponentDisplayName();
const copyCategory = node.data?.libraryCategory && node.data.libraryCategory !== 'basic'
? node.data.libraryCategory
: (node.data?.category && node.data.category !== 'basic' ? node.data.category : '');
const copiedName = generateComponentDisplayName(
copyCategory || node.data?.componentName || node.data?.elementType,
{ singularize: Boolean(copyCategory), abbreviate: Boolean(copyCategory) }
);
const copiedData = {
...node.data,
componentDisplayName: copiedName
@@ -3852,6 +3863,7 @@
const selectedNodes = activePage.nodes.filter(n => n.selected);
const selectedNodeIds = new Set(selectedNodes.map(n => n.id));
if (selectedNodeIds.size > 0) {
releaseComponentDisplayNames(selectedNodes);
const newNodes = activePage.nodes.filter(n => !selectedNodeIds.has(n.id));
const newEdges = activePage.edges.filter(e => !selectedNodeIds.has(e.source) && !selectedNodeIds.has(e.target));
setPages(prev => prev.map(p => p.id === activePage.id ? { ...p, nodes: newNodes, edges: newEdges } : p));
@@ -3899,16 +3911,122 @@
};
}, [handleCopy, handleCut, handlePaste, handleDelete, rotateComponentByNinety, getSpaceRotationTarget, clearSpaceRotateNode]);
const componentCounterRef = useRef(1);
const componentIndexesByPrefixRef = useRef({});
const generateComponentDisplayName = useCallback(() => {
const name = `component_${componentCounterRef.current}`;
componentCounterRef.current += 1;
return name;
const COMPONENT_CATEGORY_PREFIX_ABBREVIATIONS = {
directional_coupler: 'DC',
directional_couplers: 'DC',
multimode_interferometer: 'MMI',
multimode_interferometers: 'MMI',
photodetector: 'PD',
photodetectors: 'PD',
waveguide: 'WG',
waveguides: 'WG',
transition: 'TRX',
transitions: 'TRX',
transistion: 'TRX',
transistions: 'TRX',
Mach_Zender_Modulator: 'MZM',
Mach_Zender_Modulators: 'MZM',
Mach_Zender_modulator: 'MZM',
Mach_Zender_modulators: 'MZM',
mach_zender_modulator: 'MZM',
mach_zender_modulators: 'MZM',
bending: 'BD',
bendings: 'BD',
edge_coupler: 'EC',
edge_couplers: 'EC',
grating_coupler: 'GC',
grating_couplers: 'GC',
termination: 'TERM',
terminations: 'TERM'
};
function parseComponentDisplayName(displayName) {
const match = String(displayName || '').match(/^(.+)_(\d+)$/);
if (!match) return null;
const index = Number(match[2]);
if (!Number.isInteger(index) || index < 1) return null;
return { prefix: match[1], index };
}
function reserveComponentDisplayName(displayName) {
const parsed = parseComponentDisplayName(displayName);
if (!parsed) return;
const usedIndexes = componentIndexesByPrefixRef.current[parsed.prefix] || new Set();
usedIndexes.add(parsed.index);
componentIndexesByPrefixRef.current[parsed.prefix] = usedIndexes;
}
function releaseComponentDisplayName(displayName) {
const parsed = parseComponentDisplayName(displayName);
if (!parsed) return;
const usedIndexes = componentIndexesByPrefixRef.current[parsed.prefix];
if (!usedIndexes) return;
usedIndexes.delete(parsed.index);
if (usedIndexes.size === 0) {
delete componentIndexesByPrefixRef.current[parsed.prefix];
}
}
function releaseComponentDisplayNames(nodes = []) {
nodes.forEach(node => releaseComponentDisplayName(node?.data?.componentDisplayName));
}
function reserveComponentDisplayNamesFromPages() {
pages.forEach(page => {
(page.nodes || []).forEach(node => reserveComponentDisplayName(node?.data?.componentDisplayName));
});
}
const normalizeComponentDisplayNamePrefix = useCallback((prefixSource, options = {}) => {
const cleanedPrefix = String(prefixSource || 'element')
.trim()
.replace(/[\\/]+/g, '_')
.replace(/\s+/g, '_')
.replace(/[^A-Za-z0-9_]+/g, '_')
.replace(/_+/g, '_')
.replace(/^_+|_+$/g, '');
if (!cleanedPrefix) return 'element';
const abbreviation = options.abbreviate
? COMPONENT_CATEGORY_PREFIX_ABBREVIATIONS[cleanedPrefix] || COMPONENT_CATEGORY_PREFIX_ABBREVIATIONS[cleanedPrefix.toLowerCase()]
: '';
if (abbreviation) return abbreviation;
if (!options.singularize) return cleanedPrefix;
const parts = cleanedPrefix.split('_');
const lastIndex = parts.length - 1;
const last = parts[lastIndex];
if (last.length > 3 && last.endsWith('ies')) {
parts[lastIndex] = `${last.slice(0, -3)}y`;
} else if (last.length > 1 && last.endsWith('s')) {
parts[lastIndex] = last.slice(0, -1);
}
const singularPrefix = parts.join('_') || 'element';
if (options.abbreviate) {
return COMPONENT_CATEGORY_PREFIX_ABBREVIATIONS[singularPrefix] || COMPONENT_CATEGORY_PREFIX_ABBREVIATIONS[singularPrefix.toLowerCase()] || singularPrefix;
}
return singularPrefix;
}, []);
const generateComponentDisplayName = useCallback((prefixSource = 'element', options = {}) => {
const prefix = normalizeComponentDisplayNamePrefix(prefixSource, options);
reserveComponentDisplayNamesFromPages();
const usedIndexes = componentIndexesByPrefixRef.current[prefix] || new Set();
let nextIndex = 1;
while (usedIndexes.has(nextIndex)) nextIndex += 1;
const name = `${prefix}_${nextIndex}`;
usedIndexes.add(nextIndex);
componentIndexesByPrefixRef.current[prefix] = usedIndexes;
return name;
}, [normalizeComponentDisplayNamePrefix, pages]);
const renameComponent = useCallback((nodeId, newComponentDisplayName) => {
if (!activePageId) return;
const oldDisplayName = activePage?.nodes.find(node => node.id === nodeId)?.data?.componentDisplayName;
if (oldDisplayName !== newComponentDisplayName) {
releaseComponentDisplayName(oldDisplayName);
reserveComponentDisplayName(newComponentDisplayName);
}
setPages(prev => prev.map(p => {
if (p.id !== activePageId) return p;
return {
@@ -3916,7 +4034,7 @@
nodes: p.nodes.map(n => n.id === nodeId ? { ...n, data: { ...n.data, componentDisplayName: newComponentDisplayName } } : n)
};
}));
}, [activePageId]);
}, [activePageId, activePage]);
const fetchLibrary = useCallback(async () => {
try {
@@ -4788,8 +4906,7 @@
const componentName = parsedData.componentName || parsedData.name;
const basicArguments = createBasicSettings(componentName, parsedData.settings);
const metadata = getBasicComponentMetadata(componentName, basicArguments);
const componentDisplayName = `${componentName.replace(/\s+/g, '_')}_${componentCounterRef.current}`;
componentCounterRef.current += 1;
const componentDisplayName = generateComponentDisplayName(componentName);
const newNode = {
id: Date.now().toString(),
type: 'rotatableNode',
@@ -4815,8 +4932,7 @@
return;
}
if (parsedData.type === 'element') {
const elementName = parsedData.elementType === 'anchor' ? `anchor_${componentCounterRef.current}` : `port_${componentCounterRef.current}`;
componentCounterRef.current += 1;
const elementName = generateComponentDisplayName(parsedData.elementType === 'anchor' ? 'anchor' : 'port');
const isPort = parsedData.elementType === 'port';
const newNode = isPort
? {
@@ -4876,7 +4992,10 @@
return;
}
const selectedIsForge = isForgeComponent(selectedComponent);
const componentDisplayName = generateComponentDisplayName();
const componentDisplayName = generateComponentDisplayName(parsedData.category || selectedComponent, {
singularize: Boolean(parsedData.category),
abbreviate: Boolean(parsedData.category)
});
const newNode = {
id: Date.now().toString(),
type: 'rotatableNode',
@@ -4904,7 +5023,10 @@
});
return;
}
const componentDisplayName = generateComponentDisplayName();
const componentDisplayName = generateComponentDisplayName(parsedData.category || parsedData.name, {
singularize: Boolean(parsedData.category),
abbreviate: Boolean(parsedData.category)
});
const newNode = {
id: Date.now().toString(),
type: 'rotatableNode',
+26
View File
@@ -197,6 +197,32 @@ assert(
canvasHtml.includes('getSpaceRotationTarget') && canvasHtml.includes('selectedSpaceNode'),
'Space rotation should also use the currently selected component when no mouse-hold target is active'
);
assert(
canvasHtml.includes('const componentIndexesByPrefixRef = useRef({});') &&
canvasHtml.includes('const usedIndexes = componentIndexesByPrefixRef.current[prefix] || new Set();') &&
canvasHtml.includes('while (usedIndexes.has(nextIndex)) nextIndex += 1;') &&
canvasHtml.includes('usedIndexes.add(nextIndex);') &&
canvasHtml.includes('const name = `${prefix}_${nextIndex}`;') &&
canvasHtml.includes('releaseComponentDisplayNames(selectedNodes);') &&
canvasHtml.includes('releaseComponentDisplayName(oldDisplayName);') &&
canvasHtml.includes('reserveComponentDisplayName(newComponentDisplayName);') &&
!canvasHtml.includes('componentCounterRef.current') &&
!canvasHtml.includes('componentCountersByPrefixRef') &&
canvasHtml.includes('COMPONENT_CATEGORY_PREFIX_ABBREVIATIONS') &&
canvasHtml.includes("directional_coupler: 'DC'") &&
canvasHtml.includes("multimode_interferometers: 'MMI'") &&
canvasHtml.includes("photodetectors: 'PD'") &&
canvasHtml.includes("waveguides: 'WG'") &&
canvasHtml.includes("transitions: 'TRX'") &&
canvasHtml.includes("Mach_Zender_modulators: 'MZM'") &&
canvasHtml.includes("bendings: 'BD'") &&
canvasHtml.includes("edge_couplers: 'EC'") &&
canvasHtml.includes("grating_couplers: 'GC'") &&
canvasHtml.includes("terminations: 'TERM'") &&
canvasHtml.includes('abbreviate: Boolean(parsedData.category)') &&
canvasHtml.includes('abbreviate: Boolean(copyCategory)'),
'new PDK component instances should use their component category abbreviation as the display-name prefix'
);
assert(
canvasHtml.includes('normalizeAngle,') && canvasHtml.includes('normalizeAngle(Number(node.data?.rotation || 0) + 90)'),
'Space rotation should import normalizeAngle before using it'