routing for canvase level object is finished.
This commit is contained in:
Binary file not shown.
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 439 KiB |
@@ -0,0 +1,243 @@
|
||||
# =============================================
|
||||
# mxPIC Cell/Project Definition File
|
||||
# =============================================
|
||||
schema_version: "2.0.0"
|
||||
kind: cell
|
||||
coordinate_system: gds_y_up
|
||||
canvas_size:
|
||||
width: 1000
|
||||
height: 1000
|
||||
project: mxpic_project_1
|
||||
name: canvas_1
|
||||
type: composite
|
||||
version: "1.0.0"
|
||||
|
||||
# 1. External Ports (How this cell connects to the outside world)
|
||||
ports:
|
||||
- name: port
|
||||
layer: WG_CORE
|
||||
x: 200.0
|
||||
y: -370.0
|
||||
angle: 180.0
|
||||
width: 0.5
|
||||
- name: port_2
|
||||
layer: WG_CORE
|
||||
x: 200.0
|
||||
y: -370.0
|
||||
angle: 180.0
|
||||
width: 0.5
|
||||
- name: port_1
|
||||
layer: WG_CORE
|
||||
x: 200.0
|
||||
y: -420.0
|
||||
angle: 180.0
|
||||
width: 0.5
|
||||
- name: port_3
|
||||
layer: WG_CORE
|
||||
x: 691.9
|
||||
y: -267.5
|
||||
angle: 0.0
|
||||
width: 0.5
|
||||
|
||||
# 2. Instances (The sub-components dropped onto this canvas)
|
||||
instances:
|
||||
MMI_7:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 310.0
|
||||
y: -370.0
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_8:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 560.0
|
||||
y: -130.0
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_9:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 560.0
|
||||
y: -180.0
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_10:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 560.0
|
||||
y: -230.0
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_11:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 560.0
|
||||
y: -280.0
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_12:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 310.0
|
||||
y: -420.0
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
elements:
|
||||
port:
|
||||
type: port
|
||||
x: 200.0
|
||||
y: -370.0
|
||||
angle: 0.0
|
||||
port_number: 1
|
||||
pitch: 10
|
||||
layer: WG_CORE
|
||||
width: 0.5
|
||||
description: ""
|
||||
Anchor_1:
|
||||
type: anchor
|
||||
x: 460.0
|
||||
y: -300.0
|
||||
angle: 90.0
|
||||
port_number: 4
|
||||
pitch: 10
|
||||
layer: WG_CORE
|
||||
width: 0.5
|
||||
description: ""
|
||||
port_2:
|
||||
type: port
|
||||
x: 200.0
|
||||
y: -370.0
|
||||
angle: 0.0
|
||||
port_number: 1
|
||||
pitch: 10
|
||||
layer: WG_CORE
|
||||
width: 0.5
|
||||
description: ""
|
||||
port_1:
|
||||
type: port
|
||||
x: 200.0
|
||||
y: -420.0
|
||||
angle: 0.0
|
||||
port_number: 1
|
||||
pitch: 10
|
||||
layer: WG_CORE
|
||||
width: 0.5
|
||||
description: ""
|
||||
port_3:
|
||||
type: port
|
||||
x: 691.9
|
||||
y: -267.5
|
||||
angle: 180.0
|
||||
port_number: 1
|
||||
pitch: 10
|
||||
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: MMI_7:a1
|
||||
to: port_2:port
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_12:a1
|
||||
to: port_1:port
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_7:b1
|
||||
to: Anchor_1:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_7:b2
|
||||
to: Anchor_1:a2
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_12:b1
|
||||
to: Anchor_1:a3
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_12:b2
|
||||
to: Anchor_1:a4
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: Anchor_1:b4
|
||||
to: MMI_11:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: Anchor_1:b3
|
||||
to: MMI_10:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: Anchor_1:b2
|
||||
to: MMI_9:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: Anchor_1:b1
|
||||
to: MMI_8:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_11:b1
|
||||
to: port_3:port
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 538 KiB After Width: | Height: | Size: 579 KiB |
@@ -23,21 +23,10 @@ ports:
|
||||
|
||||
# 2. Instances (The sub-components dropped onto this canvas)
|
||||
instances:
|
||||
EC_1:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/edge_couplers/EC_SiN400_1310_1p0dB_L635_A0_QY_202604
|
||||
x: 0.0
|
||||
y: -2660.0
|
||||
rotation: 180.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_1:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 936.8
|
||||
y: -2358.5
|
||||
MZM_1:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/composites/Mach_Zender_modulators/MZI_SiN400_Si220_PIN_mod_1310_L1300_QY_202603
|
||||
x: 1740.0
|
||||
y: -2350.0
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
@@ -45,87 +34,10 @@ instances:
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_2:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 1089.2
|
||||
y: -2247.3
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_3:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 1096.8
|
||||
y: -2598.0
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_4:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 735.0
|
||||
y: -2541.1
|
||||
rotation: 90.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_5:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 1086.5
|
||||
y: -2097.1
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
EC_2:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/edge_couplers/EC_SiN400_1310_1p0dB_L635_A0_QY_202604
|
||||
x: 0.0
|
||||
y: -2825.7
|
||||
rotation: 180.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_6:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 913.7
|
||||
y: -2537.3
|
||||
rotation: 90.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_7:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 1027.2
|
||||
y: -2736.7
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
mirror: false
|
||||
settings:
|
||||
length:
|
||||
|
||||
MMI_8:
|
||||
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
|
||||
x: 842.6
|
||||
y: -2941.6
|
||||
canvas_1:
|
||||
component: canvas_1
|
||||
x: 903.5
|
||||
y: -2681.6
|
||||
rotation: 0.0
|
||||
flip: 0
|
||||
flop: 0
|
||||
@@ -138,86 +50,27 @@ elements:
|
||||
type: port
|
||||
x: 50.0
|
||||
y: -150.0
|
||||
angle: 0.0
|
||||
angle: 180.0
|
||||
port_number: 1
|
||||
pitch: 10
|
||||
layer: WG_CORE
|
||||
width: 0.5
|
||||
description: ""
|
||||
anchor_1:
|
||||
type: anchor
|
||||
x: 732.7
|
||||
y: -2824.7
|
||||
angle: 0.0
|
||||
port_number: 5
|
||||
pitch: 10
|
||||
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: MMI_1:b1
|
||||
to: MMI_2:a1
|
||||
- from: canvas_1:port_1
|
||||
to: MZM_1:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_1:b2
|
||||
to: MMI_3:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: EC_1:a1
|
||||
to: anchor_1:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: anchor_1:b1
|
||||
to: MMI_4:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_4:b2
|
||||
to: MMI_1:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_5:a1
|
||||
to: MMI_4:b1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: EC_2:a1
|
||||
to: anchor_1:a2
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: anchor_1:b2
|
||||
to: MMI_6:a1
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
radius: 10
|
||||
routing_type: euler_bend
|
||||
- from: MMI_8:b1
|
||||
to: MMI_7:a1
|
||||
- from: canvas_1:port_3
|
||||
to: MZM_1:a2
|
||||
xsection: strip
|
||||
family: optical
|
||||
width: 0.45
|
||||
|
||||
Binary file not shown.
+35
-10
@@ -446,7 +446,8 @@
|
||||
const vertical = side === 'left' || side === 'right';
|
||||
|
||||
return ports.map((port, index) => {
|
||||
const percent = fallbackPercent(index, ports.length);
|
||||
const explicitPercent = Number(port.info && port.info.handlePercent);
|
||||
const percent = Number.isFinite(explicitPercent) ? explicitPercent : fallbackPercent(index, ports.length);
|
||||
const percentValue = `${percent}%`;
|
||||
const style = vertical
|
||||
? { top: percentValue, transform: side === 'left' ? 'translate(-50%, -50%)' : 'translate(50%, -50%)' }
|
||||
@@ -587,6 +588,24 @@
|
||||
// Calculate the centered offset for one repeated port index.
|
||||
const elementPortOffset = (index, count, pitch) => ((count - 1) / 2 - index) * pitch;
|
||||
|
||||
// Keep Basic line-like components at twice the 10 px canvas port-circle size.
|
||||
const BASIC_LINE_COMPONENT_HEIGHT = 20;
|
||||
|
||||
// Keep Basic line-like components visually slim with a stable canvas hit area.
|
||||
const basicLineComponentHeight = () => BASIC_LINE_COMPONENT_HEIGHT;
|
||||
|
||||
// Keep bend components readable by using the radius-25 footprint as the
|
||||
// minimum canvas size, while still allowing larger radii to grow.
|
||||
const BASIC_BEND_MIN_CANVAS_RADIUS = 25;
|
||||
|
||||
// Normalize bend radius into a positive canvas footprint dimension.
|
||||
const basicBendRadiusSize = (radius) => {
|
||||
const numericRadius = Number(radius || 0);
|
||||
return Number.isFinite(numericRadius) && numericRadius > 0
|
||||
? Math.max(BASIC_BEND_MIN_CANVAS_RADIUS, numericRadius)
|
||||
: BASIC_BEND_MIN_CANVAS_RADIUS;
|
||||
};
|
||||
|
||||
// Grow port and anchor visual bodies so repeated port circles do not overlap.
|
||||
const buildElementBoxSize = (data) => {
|
||||
const portNumber = normalizePortNumber(data && data.portNumber);
|
||||
@@ -643,6 +662,7 @@
|
||||
const values = createBasicSettings(componentName, settings);
|
||||
const length = Number(values.length || 0);
|
||||
const radius = Number(values.radius || 10);
|
||||
const bendSize = basicBendRadiusSize(radius);
|
||||
const width = Number(values.width ?? values.width1 ?? 0.5);
|
||||
const xsection = values.xsection || values.xs || 'strip';
|
||||
if (componentName === 'waveguide') {
|
||||
@@ -653,14 +673,14 @@
|
||||
}
|
||||
if (componentName === '90 bend') {
|
||||
return {
|
||||
a1: { x: 0, y: 0, a: 180, width, xsection, description: 'Optical power input' },
|
||||
b1: { x: radius, y: radius, a: 90, width, xsection, description: 'Optical power output' }
|
||||
a1: { x: 0, y: bendSize / 2, a: 180, width, xsection, description: 'Optical power input' },
|
||||
b1: { x: bendSize / 2, y: 0, a: 90, width, xsection, description: 'Optical power output' }
|
||||
};
|
||||
}
|
||||
if (componentName === '180 bend') {
|
||||
return {
|
||||
a1: { x: 0, y: 0, a: 180, width, xsection, description: 'Optical power input' },
|
||||
b1: { x: 0, y: 2 * radius, a: 180, width, xsection, description: 'Optical power output' }
|
||||
b1: { x: 0, y: 2 * bendSize, a: 180, width, xsection, description: 'Optical power output' }
|
||||
};
|
||||
}
|
||||
if (componentName === 'cricle' || componentName === 'circle') {
|
||||
@@ -686,13 +706,14 @@
|
||||
const radius = Number(values.radius || 10);
|
||||
const width = Number(values.width ?? values.width1 ?? 0.5);
|
||||
const width2 = Number(values.width2 ?? width);
|
||||
const bendSize = basicBendRadiusSize(radius);
|
||||
const boxSize = componentName === 'waveguide'
|
||||
? [Math.max(length, 10), Math.max(width * 4, 4)]
|
||||
? [Math.max(length, 10), basicLineComponentHeight(width)]
|
||||
: componentName === 'taper'
|
||||
? [Math.max(length, 10), Math.max(width, width2) * 10 + 18]
|
||||
? [Math.max(length, 10), basicLineComponentHeight(width, width2)]
|
||||
: componentName === '180 bend'
|
||||
? [radius, radius * 2]
|
||||
: [radius, radius];
|
||||
? [bendSize, bendSize * 2]
|
||||
: [bendSize, bendSize];
|
||||
return {
|
||||
name: componentName,
|
||||
foundry: 'mxpic',
|
||||
@@ -703,6 +724,10 @@
|
||||
};
|
||||
};
|
||||
|
||||
// Flip an internal standalone Port angle into the outward-facing cell port
|
||||
// angle used when this canvas is placed as a component elsewhere.
|
||||
const externalPortAngle = (angle) => normalizeAngle(Number(angle ?? 0) + 180);
|
||||
|
||||
// Convert standalone port nodes into page-level layout ports.
|
||||
const buildPageComponentPorts = (port, nodes) => {
|
||||
const portNodes = (nodes || []).filter(isPortElementNode);
|
||||
@@ -723,7 +748,7 @@
|
||||
ports[exportName] = {
|
||||
x: Number(point.x || 0),
|
||||
y: Number(point.y || 0),
|
||||
a: Number(portInfo.a ?? data.angle ?? data.a ?? 0),
|
||||
a: externalPortAngle(portInfo.a ?? data.angle ?? data.a ?? 0),
|
||||
width: Number(portInfo.width || data.width || 0.5)
|
||||
};
|
||||
});
|
||||
@@ -735,7 +760,7 @@
|
||||
port: {
|
||||
x: Number(port.x || 0),
|
||||
y: Number(port.y || 0),
|
||||
a: Number(port.a || 0),
|
||||
a: externalPortAngle(port.a || 0),
|
||||
width: Number(port.width || 0.5)
|
||||
}
|
||||
};
|
||||
|
||||
+54
-18
@@ -1186,7 +1186,7 @@ Organization : OptiHK Limited
|
||||
border: 1px solid var(--floating-label-border);
|
||||
color: var(--port-label-text);
|
||||
box-shadow: 0 5px 12px rgba(0, 0, 0, 0.18);
|
||||
font-size: 0.42rem;
|
||||
font-size: 0.3rem;
|
||||
line-height: 1.2;
|
||||
font-family: 'IBM Plex Mono', Consolas, Monaco, monospace;
|
||||
white-space: nowrap;
|
||||
@@ -1278,6 +1278,10 @@ Organization : OptiHK Limited
|
||||
box-shadow: var(--floating-label-shadow);
|
||||
}
|
||||
|
||||
.canvas-text-hidden .component-floating-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.light-mode .port-name-label {
|
||||
background: var(--port-label-bg);
|
||||
border-color: var(--floating-label-border);
|
||||
@@ -1312,14 +1316,14 @@ Organization : OptiHK Limited
|
||||
}
|
||||
|
||||
.component-floating-label strong {
|
||||
font-size: 0.52rem;
|
||||
font-size: 0.4rem;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.component-floating-label span {
|
||||
margin-top: 1px;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.44rem;
|
||||
font-size: 0.32rem;
|
||||
}
|
||||
|
||||
.canvas-size-panel {
|
||||
@@ -1379,7 +1383,7 @@ Organization : OptiHK Limited
|
||||
background: rgba(9, 18, 28, 0.94);
|
||||
color: #e2f7f3;
|
||||
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.3);
|
||||
font: 600 0.62rem/1.35 'IBM Plex Mono', Consolas, Monaco, monospace;
|
||||
font: 600 0.5rem/1.35 'IBM Plex Mono', Consolas, Monaco, monospace;
|
||||
text-align: center;
|
||||
transform: translate(-50%, -50%);
|
||||
pointer-events: none;
|
||||
@@ -1594,7 +1598,7 @@ Organization : OptiHK Limited
|
||||
}, [id, data.ports, data.componentName, data.boxSize]);
|
||||
|
||||
const baseHandleStyle = {
|
||||
width: 10, height: 10,
|
||||
width: 8, height: 8,
|
||||
background: 'var(--bg-main)',
|
||||
border: '2px solid var(--accent)',
|
||||
borderRadius: '50%',
|
||||
@@ -1611,6 +1615,7 @@ Organization : OptiHK Limited
|
||||
);
|
||||
const componentSize = normalizeBoxSize({ box_size: data.boxSize }, DEFAULT_COMPONENT_BOX_SIZE);
|
||||
const isAnchorElement = data.elementType === 'anchor';
|
||||
const isBasicCompactComponent = isBasicComponent(data.componentName) && ['waveguide', 'taper', '90 bend'].includes(data.componentName);
|
||||
const visualSize = isAnchorElement ? { width: PORT_NODE_SIZE, height: PORT_NODE_SIZE } : componentSize;
|
||||
const iconSize = createComponentSymbolMetrics(componentSize);
|
||||
const portLabelStyle = (portHandle) => {
|
||||
@@ -1643,9 +1648,16 @@ Organization : OptiHK Limited
|
||||
style={{
|
||||
width: componentSize.width,
|
||||
height: visualSize.height,
|
||||
minHeight: visualSize.height,
|
||||
border: selected ? '2px solid var(--accent)' : '1px solid var(--border)',
|
||||
transform: `rotate(${data.rotation || 0}deg) scaleX(${data.flop ? -1 : 1}) scaleY(${data.flip ? -1 : 1})`,
|
||||
boxShadow: selected ? '0 0 15px rgba(56, 189, 248, 0.2)' : '0 4px 6px rgba(0,0,0,0.3)',
|
||||
...(isBasicCompactComponent ? {
|
||||
padding: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
} : {}),
|
||||
...(isAnchorElement ? {
|
||||
width: PORT_NODE_SIZE,
|
||||
minHeight: PORT_NODE_SIZE,
|
||||
@@ -1658,7 +1670,7 @@ Organization : OptiHK Limited
|
||||
}}
|
||||
>
|
||||
{isAnchorElement ? (
|
||||
<span style={{ fontSize: 10, fontWeight: 800, color: selected ? 'var(--accent)' : 'var(--text-main)' }}>A</span>
|
||||
<span style={{ fontSize: 8, fontWeight: 800, color: selected ? 'var(--accent)' : 'var(--text-main)' }}>A</span>
|
||||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '8px', minHeight: '100%' }}>
|
||||
{!data.hideIcon && data.category && (
|
||||
@@ -1733,8 +1745,8 @@ Organization : OptiHK Limited
|
||||
};
|
||||
const baseHandleStyle = {
|
||||
background: 'var(--accent)',
|
||||
width: 8,
|
||||
height: 8
|
||||
width: 6,
|
||||
height: 6
|
||||
};
|
||||
return (
|
||||
<div style={{
|
||||
@@ -1743,7 +1755,7 @@ Organization : OptiHK Limited
|
||||
border: selected ? '2px solid white' : '2px solid var(--accent)',
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
|
||||
color: selected ? 'white' : 'var(--accent)',
|
||||
fontSize: 10, fontWeight: 'bold',
|
||||
fontSize: 8, fontWeight: 'bold',
|
||||
boxShadow: selected ? '0 0 10px rgba(56,189,248,0.4)' : 'none',
|
||||
transform: `rotate(${angle}deg)`,
|
||||
}}>
|
||||
@@ -1783,8 +1795,8 @@ Organization : OptiHK Limited
|
||||
bottom: Position.Bottom
|
||||
};
|
||||
const baseHandleStyle = {
|
||||
width: 8,
|
||||
height: 8,
|
||||
width: 6,
|
||||
height: 6,
|
||||
background: 'var(--accent)',
|
||||
border: '1px solid var(--bg-main)',
|
||||
borderRadius: '50%'
|
||||
@@ -2729,6 +2741,7 @@ Organization : OptiHK Limited
|
||||
const selectedNodeBoxSize = selectedNode?.data?.componentName && !selectedNode?.data?.elementType
|
||||
? normalizeBoxSize({ box_size: selectedNode.data?.boxSize }, DEFAULT_COMPONENT_BOX_SIZE)
|
||||
: null;
|
||||
const xsections = Object.keys((technologyManifest || FALLBACK_TECHNOLOGY_MANIFEST).xsections || {});
|
||||
|
||||
const selectedRouteEdges = selectedEdges.length > 0 ? selectedEdges : (selectedEdge ? [selectedEdge] : []);
|
||||
if (selectedRouteEdges.length > 0) {
|
||||
@@ -2744,7 +2757,6 @@ Organization : OptiHK Limited
|
||||
radius: mixedValue('radius'),
|
||||
routing_type: mixedValue('routing_type')
|
||||
};
|
||||
const xsections = Object.keys((technologyManifest || FALLBACK_TECHNOLOGY_MANIFEST).xsections || {});
|
||||
const routingTypes = (technologyManifest || FALLBACK_TECHNOLOGY_MANIFEST).routing_types || ['euler_bend', 'standard_bend'];
|
||||
return (
|
||||
<aside style={{
|
||||
@@ -3164,12 +3176,26 @@ Organization : OptiHK Limited
|
||||
{Object.entries(basicArguments).map(([key, value]) => (
|
||||
<label key={key} style={{ display: 'grid', gap: 4 }}>
|
||||
<span style={{ color: 'var(--text-muted)' }}>{key}</span>
|
||||
<input
|
||||
type={typeof value === 'number' ? 'number' : 'text'}
|
||||
step="any"
|
||||
value={value ?? ''}
|
||||
onChange={(event) => updateBasicArgument(key, event.target.value)}
|
||||
/>
|
||||
{key === 'xsection' ? (
|
||||
<select
|
||||
value={value ?? ''}
|
||||
onChange={(event) => updateBasicArgument(key, event.target.value)}
|
||||
>
|
||||
{value && !xsections.includes(value) && (
|
||||
<option value={value}>{value}</option>
|
||||
)}
|
||||
{xsections.map(xsection => (
|
||||
<option key={xsection} value={xsection}>{xsection}</option>
|
||||
))}
|
||||
</select>
|
||||
) : (
|
||||
<input
|
||||
type={typeof value === 'number' ? 'number' : 'text'}
|
||||
step="any"
|
||||
value={value ?? ''}
|
||||
onChange={(event) => updateBasicArgument(key, event.target.value)}
|
||||
/>
|
||||
)}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
@@ -3461,6 +3487,7 @@ Organization : OptiHK Limited
|
||||
const [dragging, setDragging] = useState(null);
|
||||
|
||||
const [gridSnap, setGridSnap] = useState(false);
|
||||
const [canvasTextVisible, setCanvasTextVisible] = useState(true);
|
||||
const [themeMode, setThemeMode] = useState(() => localStorage.getItem('mxpic-theme') || 'dark');
|
||||
const [logs, setLogs] = useState([{ time: new Date().toLocaleTimeString(), message: 'Editor ready.' }]);
|
||||
const [buildLayoutBusy, setBuildLayoutBusy] = useState(false);
|
||||
@@ -5463,6 +5490,11 @@ Organization : OptiHK Limited
|
||||
setGridSnap(prev => !prev);
|
||||
}, []);
|
||||
|
||||
// Toggle the instance name/PDK labels shown above canvas components.
|
||||
const toggleCanvasText = useCallback(() => {
|
||||
setCanvasTextVisible(prev => !prev);
|
||||
}, []);
|
||||
|
||||
// Toggle the measurement ruler and clear partial measurements.
|
||||
const toggleRulerMode = useCallback(() => {
|
||||
setRulerMode(prev => {
|
||||
@@ -5999,6 +6031,9 @@ ${bundlesBlock}`;
|
||||
transition: 'transform 0.2s',
|
||||
}} />
|
||||
</div>
|
||||
<button className="mini-btn" onClick={toggleCanvasText} aria-pressed={canvasTextVisible ? 'true' : 'false'}>
|
||||
{canvasTextVisible ? 'Text On' : 'Text Off'}
|
||||
</button>
|
||||
<details className="link-mode-tabs" title="Route type used for new links">
|
||||
<summary className="link-mode-summary">
|
||||
<span className="link-mode-label">Link</span>
|
||||
@@ -6069,6 +6104,7 @@ ${bundlesBlock}`;
|
||||
<LayoutSvgPreview page={activePage} />
|
||||
) : (
|
||||
<ReactFlow
|
||||
className={canvasTextVisible ? '' : 'canvas-text-hidden'}
|
||||
nodes={renderNodes}
|
||||
edges={renderEdges}
|
||||
onNodesChange={onNodesChange}
|
||||
|
||||
@@ -172,18 +172,46 @@ assert.deepStrictEqual(
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
helpers.getBasicComponentMetadata('waveguide', { length: 120, width: 0.5 }).box_size,
|
||||
[120, 4],
|
||||
'basic waveguide symbol should use a narrow default height'
|
||||
[120, 20],
|
||||
'basic waveguide symbol should use a height that is two port-circle diameters'
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
helpers.getBasicComponentMetadata('90 bend', { radius: 15 }).box_size,
|
||||
[15, 15],
|
||||
'90 bend symbol should be square with side length equal to radius'
|
||||
[25, 25],
|
||||
'90 bend symbol should not shrink below the radius-25 canvas size'
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
helpers.getBasicComponentMetadata('90 bend', { radius: 30 }).box_size,
|
||||
[30, 30],
|
||||
'90 bend symbol should still scale above radius 25'
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
helpers.buildBasicComponentPorts('90 bend', { radius: 15, width: 0.6 }),
|
||||
{
|
||||
a1: { x: 0, y: 12.5, a: 180, width: 0.6, xsection: 'strip', description: 'Optical power input' },
|
||||
b1: { x: 12.5, y: 0, a: 90, width: 0.6, xsection: 'strip', description: 'Optical power output' }
|
||||
},
|
||||
'90 bend ports should sit at the middle of the left and top sides of the square'
|
||||
);
|
||||
const ninetyBendHandles = helpers.buildPortHandles(helpers.buildBasicComponentPorts('90 bend', { radius: 15 }));
|
||||
assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'a1').position, 'left');
|
||||
assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'a1').style.top, '50%');
|
||||
assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'b1').position, 'top');
|
||||
assert.strictEqual(ninetyBendHandles.find(handle => handle.name === 'b1').style.left, '50%');
|
||||
assert.deepStrictEqual(
|
||||
helpers.getBasicComponentMetadata('180 bend', { radius: 15 }).box_size,
|
||||
[15, 30],
|
||||
'180 bend symbol should be one radius wide and two radii tall'
|
||||
[25, 50],
|
||||
'180 bend symbol should not shrink below the radius-25 canvas size'
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
helpers.getBasicComponentMetadata('180 bend', { radius: 30 }).box_size,
|
||||
[30, 60],
|
||||
'180 bend symbol should still scale above radius 25'
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
helpers.getBasicComponentMetadata('taper', { length: 80, width1: 0.4, width2: 1.2 }).box_size,
|
||||
[80, 20],
|
||||
'basic taper symbol should use a height that is two port-circle diameters'
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
helpers.getBasicComponentMetadata('taper', { length: 80, width1: 0.4, width2: 1.2 }).ports.a1.description,
|
||||
@@ -259,11 +287,11 @@ const pagePortsYaml = helpers.buildPortsYaml({ x: 50, y: 150, a: 90 });
|
||||
assert(pagePortsYaml.includes('- name: port'));
|
||||
assert(pagePortsYaml.includes('x: 50.0'));
|
||||
assert(pagePortsYaml.includes('y: -150.0'));
|
||||
assert(pagePortsYaml.includes('angle: 90.0'));
|
||||
assert(pagePortsYaml.includes('angle: -90.0'));
|
||||
|
||||
const componentPorts = helpers.buildPageComponentPorts({ x: 12, y: -6, a: 180 });
|
||||
assert.deepStrictEqual(componentPorts, {
|
||||
port: { x: 12, y: -6, a: 180, width: 0.5 }
|
||||
port: { x: 12, y: -6, a: 0, width: 0.5 }
|
||||
});
|
||||
|
||||
const elementNodes = [
|
||||
@@ -341,9 +369,9 @@ assert.deepStrictEqual(
|
||||
data: { componentDisplayName: 'array', elementType: 'port', portNumber: 3, pitch: 10, width: 0.6 }
|
||||
}]),
|
||||
{
|
||||
array_1: { x: 100, y: 190, a: 0, width: 0.6 },
|
||||
array_2: { x: 100, y: 200, a: 0, width: 0.6 },
|
||||
array_3: { x: 100, y: 210, a: 0, width: 0.6 }
|
||||
array_1: { x: 100, y: 190, a: 180, width: 0.6 },
|
||||
array_2: { x: 100, y: 200, a: 180, width: 0.6 },
|
||||
array_3: { x: 100, y: 210, a: 180, width: 0.6 }
|
||||
}
|
||||
);
|
||||
|
||||
@@ -352,6 +380,7 @@ assert(canvasPortsYaml.includes('name: in0'));
|
||||
assert(canvasPortsYaml.includes('description: "input port"'));
|
||||
assert(canvasPortsYaml.includes('width: 0.7'));
|
||||
assert(canvasPortsYaml.includes('y: -20.0'));
|
||||
assert(canvasPortsYaml.includes('angle: 0.0'));
|
||||
|
||||
const elementsYaml = helpers.buildElementsYaml(elementNodes);
|
||||
assert(elementsYaml.includes('in0:'));
|
||||
@@ -372,7 +401,7 @@ assert(instancesWithoutElements.includes('component_1:'));
|
||||
assert(instancesWithoutElements.includes('y: -60.0'));
|
||||
|
||||
const multiPortComponentPorts = helpers.buildPageComponentPorts(null, elementNodes);
|
||||
assert.deepStrictEqual(multiPortComponentPorts.in0, { x: 10, y: 20, a: 180, width: 0.7 });
|
||||
assert.deepStrictEqual(multiPortComponentPorts.in0, { x: 10, y: 20, a: 0, width: 0.7 });
|
||||
|
||||
const technologyManifest = {
|
||||
defaults: { xsection: 'strip', width: 0.45, radius: 10, routing_type: 'euler_bend' },
|
||||
|
||||
@@ -282,6 +282,15 @@ assert(
|
||||
canvasHtml.includes('component-floating-label') && canvasHtml.includes('component-visual-body'),
|
||||
'component labels should float outside the rotated body'
|
||||
);
|
||||
assert(
|
||||
canvasHtml.includes('canvasTextVisible') &&
|
||||
canvasHtml.includes('toggleCanvasText') &&
|
||||
canvasHtml.includes('Text On') &&
|
||||
canvasHtml.includes('Text Off') &&
|
||||
canvasHtml.includes('canvas-text-hidden') &&
|
||||
canvasHtml.includes('.canvas-text-hidden .component-floating-label'),
|
||||
'canvas toolbar should toggle instance name and PDK text above components'
|
||||
);
|
||||
assert(
|
||||
canvasHtml.includes('--floating-label-bg') && canvasHtml.includes('--port-label-bg') && canvasHtml.includes('--mini-button-bg'),
|
||||
'theme variables should keep labels, port chips, and header buttons readable in light and dark modes'
|
||||
@@ -400,6 +409,34 @@ assert(
|
||||
canvasHtml.includes("isUserCell ? 'compact-tree-card' : ''"),
|
||||
'Basic, Port, and Anchor entries should render as consistent 2D cards instead of compact list rows'
|
||||
);
|
||||
assert(
|
||||
canvasHtml.includes("key === 'xsection'") &&
|
||||
canvasHtml.includes('<select') &&
|
||||
canvasHtml.includes('xsections.map(xsection =>') &&
|
||||
canvasHtml.includes('updateBasicArgument(key, event.target.value)'),
|
||||
'Basic component xsection should be selected from technology manifest xsections instead of free text'
|
||||
);
|
||||
assert(
|
||||
canvasHtml.indexOf('const xsections = Object.keys') <
|
||||
canvasHtml.indexOf('if (selectedRouteEdges.length > 0)'),
|
||||
'Basic and route property panels should share the same xsection list from RightPanel scope'
|
||||
);
|
||||
assert(
|
||||
canvasHtml.includes("['waveguide', 'taper', '90 bend'].includes(data.componentName)") &&
|
||||
canvasHtml.includes('minHeight: visualSize.height') &&
|
||||
canvasHtml.includes('isBasicCompactComponent ?'),
|
||||
'waveguide, taper, and 90 bend nodes should override the default component min-height and padding on the canvas'
|
||||
);
|
||||
assert(
|
||||
canvasHtml.includes('font-size: 0.3rem;') &&
|
||||
canvasHtml.includes('font-size: 0.4rem;') &&
|
||||
canvasHtml.includes('font-size: 0.32rem;') &&
|
||||
canvasHtml.includes("font: 600 0.5rem/1.35") &&
|
||||
canvasHtml.includes('width: 8, height: 8') &&
|
||||
canvasHtml.includes('width: 6,') &&
|
||||
canvasHtml.includes('fontSize: 8'),
|
||||
'canvas labels and port circles should render smaller than the previous sizing'
|
||||
);
|
||||
assert(
|
||||
canvasHtml.includes('ParallelRouteEdge') &&
|
||||
canvasHtml.includes('parallelOffset') &&
|
||||
|
||||
Reference in New Issue
Block a user