1 Commits

Author SHA1 Message Date
xsxx03-art 2846899097 rotation bug fixed 2026-06-08 18:54:42 +08:00
22 changed files with 1629 additions and 678 deletions
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -19
View File
@@ -1,6 +1,6 @@
# -----------------------------------------------------------------------------
# Description: Flask backend API server for authentication, project management, PDK library access, layout preview, and GDS build endpoints.
# Inside functions: no_cache_response, login_required_json, wrapper, request_ip, record_action, safe_name, user_layout_root, project_root, cell_file_path, cell_svg_path, file_version, cell_routes_path, write_route_points_sidecar, project_gds_path, technology_manifest_path_for_project, current_pdk_root, scoped_pdk_root_for_project, pdk_root_for_request_project, project_meta_path, read_project_meta
# Inside functions: no_cache_response, login_required_json, wrapper, request_ip, record_action, safe_name, user_layout_root, project_root, cell_file_path, cell_svg_path, cell_routes_path, write_route_points_sidecar, project_gds_path, technology_manifest_path_for_project, current_pdk_root, scoped_pdk_root_for_project, pdk_root_for_request_project, project_meta_path, read_project_meta
# Developer : Qin Yue @ 2026
# Organization : OptiHK Limited
# -----------------------------------------------------------------------------
@@ -9,7 +9,6 @@ import os
import re
import shutil
import json
import uuid
import yaml
from collections import OrderedDict
from functools import wraps
@@ -136,12 +135,6 @@ 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 file_version(path):
"""Return a cache-busting version token for a completed file."""
stat = os.stat(path)
return f"{stat.st_mtime_ns}-{stat.st_size}"
def cell_routes_path(project_name, cell_name):
"""Return the route sidecar JSON path for a project cell."""
return os.path.join(project_root(project_name), f"{safe_name(cell_name, 'canvas_1')}.routes.yml")
@@ -746,31 +739,24 @@ def save_layout():
write_route_points_sidecar(content, cell_routes_path(project, cell))
svg_path = None
svg_version = None
preview_status = "not_requested"
preview_error = None
if create_preview:
svg_path = cell_svg_path(project, cell)
temp_svg_path = f"{svg_path}.building-{os.getpid()}-{uuid.uuid4().hex}.svg"
try:
create_routed_layout_svg(
content,
temp_svg_path,
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),
)
os.replace(temp_svg_path, svg_path)
svg_version = file_version(svg_path)
preview_status = "generated"
except RouterStackUnavailable as e:
preview_status = "skipped"
preview_error = str(e)
svg_path = None
finally:
if os.path.exists(temp_svg_path):
os.remove(temp_svg_path)
record_action('layout.save', project=project, cell=cell, detail={"bytes": len(content), "svg": svg_path})
return jsonify({
@@ -779,9 +765,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, v=svg_version) if svg_path else None,
"svg_ready": bool(svg_path and svg_version),
"svg_version": svg_version,
"svg_url": url_for('get_layout_svg', project_name=project, cell_name=cell) if svg_path else None,
"preview_status": preview_status,
"preview_error": preview_error
}), 200
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 319 KiB

@@ -1,211 +0,0 @@
# =============================================
# mxPIC Cell/Project Definition File
# =============================================
schema_version: "2.0.0"
kind: cell
coordinate_system: gds_y_up
canvas_size:
width: 500
height: 600
project: mxpic_project_1
name: canvas_1
type: composite
version: "1.0.0"
# 1. External Ports (How this cell connects to the outside world)
pins:
- name: port_io1
layer: WG_CORE
element: port
pin: io1
x: 40.0
y: -90.0
angle: 180.0
width: 0.5
- name: port_1_io1
layer: WG_CORE
element: port_1
pin: io1
x: 410.0
y: -35.0
angle: 0.0
width: 0.5
- name: port_1_io2
layer: WG_CORE
element: port_1
pin: io2
x: 410.0
y: -25.0
angle: 0.0
width: 0.5
- name: port_2_io1
layer: WG_CORE
element: port_2
pin: io1
x: 390.0
y: -215.0
angle: 0.0
width: 0.5
- name: port_2_io2
layer: WG_CORE
element: port_2
pin: io2
x: 390.0
y: -205.0
angle: 0.0
width: 0.5
# 2. Instances (The sub-components dropped onto this canvas)
instances:
MMI_1:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
x: 130.0
y: -90.0
rotation: 0.0
flip: 0
flop: 0
mirror: false
settings:
length:
MMI_2:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
x: 280.0
y: -30.0
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: 320.1
y: -144.7
rotation: 0.0
flip: 0
flop: 0
mirror: false
settings:
length:
elements:
port:
type: port
x: 40.0
y: -90.0
angle: 180.0
pin_number: 1
pitch: 10
layer: WG_CORE
width: 0.5
description: ""
pins:
- name: port_io1
role: io1
port:
type: port
x: 40.0
y: -90.0
angle: 0.0
pin_number: 1
pitch: 10
layer: WG_CORE
width: 0.5
description: ""
pins:
- name: port_io1
role: io1
port_1:
type: port
x: 410.0
y: -30.0
angle: 180.0
pin_number: 2
pitch: 10
layer: WG_CORE
width: 0.5
description: ""
pins:
- name: port_1_io1
role: io1
- name: port_1_io2
role: io2
port_2:
type: port
x: 390.0
y: -210.0
angle: 180.0
pin_number: 2
pitch: 10
layer: WG_CORE
width: 0.5
description: ""
pins:
- name: port_2_io1
role: io1
- name: port_2_io2
role: io2
# 3. Bundles (Grouped links for multi-bus/parallel routing)
bundles:
output_bus:
routing_type: euler_bend
links:
- from: MMI_1:a1
to: port:port_io1
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: MMI_2:a1
to: MMI_1:b1
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: MMI_3:a1
to: MMI_1:b2
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: MMI_2:b1
to: port_1:port_1_io1
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: MMI_2:b1
to: port_1:port_1_io2
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: MMI_2:b2
to: port_1:port_1_io1
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: MMI_3:b1
to: port_2:port_2_io2
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
- from: MMI_3:b2
to: port_2:port_2_io1
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
@@ -1,90 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<<<<<<< HEAD
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="3218.6" height="2963.6" viewBox="14680.2 22644.7 3218.6 2963.6">
<defs>
<style type="text/css">
.l275d0 {stroke: #654522; fill: #654522; fill-opacity: 0.5;}
.l1200d0 {stroke: #F38400; fill: #F38400; fill-opacity: 0.5;}
.l101d251 {stroke: #848482; fill: #848482; fill-opacity: 0.5;}
.l1205d0 {stroke: #008856; fill: #008856; fill-opacity: 0.5;}
.l1001t0 {stroke: none; fill: #A1CAF1;}
</style>
<g id="1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2">
<polygon id="000002B6365DCE60" class="l1200d0" points="-285,-147 585,-147 585,147 -285,147"/>
<polygon id="000002B6365DCB50" class="l1205d0" points="-281.5,3.5 -288.5,3.5 -288.5,-3.5 -281.5,-3.5"/>
<polygon id="000002B6365DD020" class="l1205d0" points="581.5,40 588.5,40 588.5,47 581.5,47"/>
<polygon id="000002B6365DCCA0" class="l1205d0" points="581.5,-47 588.5,-47 588.5,-40 581.5,-40"/>
<text id="000002B605621870" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(-285 0) scale(1 -1)">a1</text>
<text id="000002B605621240" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(585 43.5) scale(1 -1)">b1</text>
<text id="000002B605622830" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(585 -43.5) scale(1 -1)">b2</text>
<text id="000002B605621D80" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(0 0) scale(1 -1)">a0</text>
<text id="000002B605621750" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(0 0) scale(1 -1)">b0</text>
</g>
<g id="1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2_405">
<polygon id="000002B63618CA50" class="l1200d0" points="-285,-147 585,-147 585,147 -285,147"/>
<polygon id="000002B605652430" class="l1205d0" points="-281.5,3.5 -288.5,3.5 -288.5,-3.5 -281.5,-3.5"/>
<polygon id="000002B605651940" class="l1205d0" points="581.5,40 588.5,40 588.5,47 581.5,47"/>
<polygon id="000002B605651BE0" class="l1205d0" points="581.5,-47 588.5,-47 588.5,-40 581.5,-40"/>
<text id="000002B605620E50" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(-285 0) scale(1 -1)">a1</text>
<text id="000002B6056216C0" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(585 43.5) scale(1 -1)">b1</text>
<text id="000002B605622680" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(585 -43.5) scale(1 -1)">b2</text>
<text id="000002B605622B00" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(0 0) scale(1 -1)">a0</text>
<text id="000002B605621E10" class="l1001t0" text-anchor="start" dominant-baseline="text-before-edge" transform="translate(0 0) scale(1 -1)">b0</text>
</g>
</defs>
<rect x="14680.2" y="22644.7" width="3218.6" height="2963.6" fill="#222222" stroke="none"/>
<g id="mxpic_project_1" transform="scale(1 -1)">
<polygon id="000002B6365DC8B0" class="l275d0" points="16679,-25360.75 15700,-25360.75 15700,-25356.25 16679,-25356.25"/>
<polygon id="000002B6365DC610" class="l275d0" points="16679,-25360.75 15700,-25360.75 15700,-25356.25 16679,-25356.25"/>
<polygon id="000002B6365DC290" class="l275d0" points="16679,-25360.75 15700,-25360.75 15700,-25356.25 16679,-25356.25"/>
<polygon id="000002B6365DC370" class="l275d0" points="16679,-25360.75 15700,-25360.75 15700,-25356.25 16679,-25356.25"/>
<polygon id="000002B6365DC300" class="l275d0" points="16679,-25360.75 15700,-25360.75 15700,-25356.25 16679,-25356.25"/>
<polygon id="000002B6365DCAE0" class="l275d0" points="16679,-25360.75 15700,-25360.75 15700,-25356.25 16679,-25356.25"/>
<polygon id="000002B6365DC680" class="l101d251" points="16719,-25400.75 15660,-25400.75 15660,-25316.25 16719,-25316.25"/>
<polygon id="000002B6365DCF40" class="l101d251" points="16719,-25400.75 15660,-25400.75 15660,-25316.25 16719,-25316.25"/>
<polygon id="000002B6365DC6F0" class="l101d251" points="16719,-25400.75 15660,-25400.75 15660,-25316.25 16719,-25316.25"/>
<polygon id="000002B6365DCFB0" class="l101d251" points="16719,-25400.75 15660,-25400.75 15660,-25316.25 16719,-25316.25"/>
<polygon id="000002B6365DC760" class="l101d251" points="16719,-25400.75 15660,-25400.75 15660,-25316.25 16719,-25316.25"/>
<polygon id="000002B6365DC140" class="l101d251" points="16719,-25400.75 15660,-25400.75 15660,-25316.25 16719,-25316.25"/>
<polygon id="000002B6365DC1B0" class="l275d0" points="16776.75,-25258.5 16781.25,-25258.5 16781.24,-25259.91 16781.17,-25262.73 16781.01,-25265.54 16780.78,-25268.35 16780.47,-25271.15 16780.08,-25273.94 16779.62,-25276.72 16779.08,-25279.48 16778.46,-25282.23 16777.77,-25284.97 16777,-25287.68 16776.16,-25290.37 16775.25,-25293.03 16774.26,-25295.67 16773.2,-25298.28 16772.07,-25300.86 16770.86,-25303.41 16769.59,-25305.92 16768.25,-25308.4 16766.84,-25310.84 16765.37,-25313.24 16763.82,-25315.6 16762.22,-25317.92 16760.55,-25320.19 16758.82,-25322.41 16757.03,-25324.59 16755.18,-25326.71 16753.27,-25328.78 16751.3,-25330.8 16749.28,-25332.77 16747.21,-25334.68 16745.09,-25336.53 16742.91,-25338.32 16740.69,-25340.05 16738.42,-25341.72 16736.1,-25343.32 16733.74,-25344.87 16731.34,-25346.34 16728.9,-25347.75 16726.42,-25349.09 16723.91,-25350.36 16721.36,-25351.57 16718.78,-25352.7 16716.17,-25353.76 16713.53,-25354.75 16710.87,-25355.66 16708.18,-25356.5 16705.47,-25357.27 16702.73,-25357.96 16699.98,-25358.58 16697.22,-25359.12 16694.44,-25359.58 16691.65,-25359.97 16688.85,-25360.28 16686.04,-25360.51 16683.23,-25360.67 16680.41,-25360.74 16679,-25360.75 16679,-25356.25 16680.35,-25356.24 16683.04,-25356.17 16685.73,-25356.02 16688.41,-25355.8 16691.09,-25355.5 16693.76,-25355.13 16696.42,-25354.69 16699.06,-25354.17 16701.69,-25353.58 16704.3,-25352.92 16706.89,-25352.19 16709.46,-25351.39 16712.01,-25350.51 16714.53,-25349.57 16717.03,-25348.55 16719.5,-25347.47 16721.93,-25346.32 16724.34,-25345.1 16726.71,-25343.82 16729.04,-25342.48 16731.33,-25341.06 16733.59,-25339.59 16735.8,-25338.06 16737.97,-25336.46 16740.1,-25334.81 16742.18,-25333.09 16744.21,-25331.32 16746.19,-25329.5 16748.12,-25327.62 16750,-25325.69 16751.82,-25323.71 16753.59,-25321.68 16755.31,-25319.6 16756.96,-25317.47 16758.56,-25315.3 16760.09,-25313.09 16761.56,-25310.83 16762.98,-25308.54 16764.32,-25306.21 16765.6,-25303.84 16766.82,-25301.43 16767.97,-25299 16769.05,-25296.53 16770.07,-25294.03 16771.01,-25291.51 16771.89,-25288.96 16772.69,-25286.39 16773.42,-25283.8 16774.08,-25281.19 16774.67,-25278.56 16775.19,-25275.92 16775.63,-25273.26 16776,-25270.59 16776.3,-25267.91 16776.52,-25265.23 16776.67,-25262.54 16776.74,-25259.85"/>
<polygon id="000002B6365DC3E0" class="l275d0" points="16776.75,-25258.5 16781.25,-25258.5 16781.24,-25259.91 16781.17,-25262.73 16781.01,-25265.54 16780.78,-25268.35 16780.47,-25271.15 16780.08,-25273.94 16779.62,-25276.72 16779.08,-25279.48 16778.46,-25282.23 16777.77,-25284.97 16777,-25287.68 16776.16,-25290.37 16775.25,-25293.03 16774.26,-25295.67 16773.2,-25298.28 16772.07,-25300.86 16770.86,-25303.41 16769.59,-25305.92 16768.25,-25308.4 16766.84,-25310.84 16765.37,-25313.24 16763.82,-25315.6 16762.22,-25317.92 16760.55,-25320.19 16758.82,-25322.41 16757.03,-25324.59 16755.18,-25326.71 16753.27,-25328.78 16751.3,-25330.8 16749.28,-25332.77 16747.21,-25334.68 16745.09,-25336.53 16742.91,-25338.32 16740.69,-25340.05 16738.42,-25341.72 16736.1,-25343.32 16733.74,-25344.87 16731.34,-25346.34 16728.9,-25347.75 16726.42,-25349.09 16723.91,-25350.36 16721.36,-25351.57 16718.78,-25352.7 16716.17,-25353.76 16713.53,-25354.75 16710.87,-25355.66 16708.18,-25356.5 16705.47,-25357.27 16702.73,-25357.96 16699.98,-25358.58 16697.22,-25359.12 16694.44,-25359.58 16691.65,-25359.97 16688.85,-25360.28 16686.04,-25360.51 16683.23,-25360.67 16680.41,-25360.74 16679,-25360.75 16679,-25356.25 16680.35,-25356.24 16683.04,-25356.17 16685.73,-25356.02 16688.41,-25355.8 16691.09,-25355.5 16693.76,-25355.13 16696.42,-25354.69 16699.06,-25354.17 16701.69,-25353.58 16704.3,-25352.92 16706.89,-25352.19 16709.46,-25351.39 16712.01,-25350.51 16714.53,-25349.57 16717.03,-25348.55 16719.5,-25347.47 16721.93,-25346.32 16724.34,-25345.1 16726.71,-25343.82 16729.04,-25342.48 16731.33,-25341.06 16733.59,-25339.59 16735.8,-25338.06 16737.97,-25336.46 16740.1,-25334.81 16742.18,-25333.09 16744.21,-25331.32 16746.19,-25329.5 16748.12,-25327.62 16750,-25325.69 16751.82,-25323.71 16753.59,-25321.68 16755.31,-25319.6 16756.96,-25317.47 16758.56,-25315.3 16760.09,-25313.09 16761.56,-25310.83 16762.98,-25308.54 16764.32,-25306.21 16765.6,-25303.84 16766.82,-25301.43 16767.97,-25299 16769.05,-25296.53 16770.07,-25294.03 16771.01,-25291.51 16771.89,-25288.96 16772.69,-25286.39 16773.42,-25283.8 16774.08,-25281.19 16774.67,-25278.56 16775.19,-25275.92 16775.63,-25273.26 16776,-25270.59 16776.3,-25267.91 16776.52,-25265.23 16776.67,-25262.54 16776.74,-25259.85"/>
<polygon id="000002B6365DC450" class="l275d0" points="16776.75,-25258.5 16781.25,-25258.5 16781.24,-25259.91 16781.17,-25262.73 16781.01,-25265.54 16780.78,-25268.35 16780.47,-25271.15 16780.08,-25273.94 16779.62,-25276.72 16779.08,-25279.48 16778.46,-25282.23 16777.77,-25284.97 16777,-25287.68 16776.16,-25290.37 16775.25,-25293.03 16774.26,-25295.67 16773.2,-25298.28 16772.07,-25300.86 16770.86,-25303.41 16769.59,-25305.92 16768.25,-25308.4 16766.84,-25310.84 16765.37,-25313.24 16763.82,-25315.6 16762.22,-25317.92 16760.55,-25320.19 16758.82,-25322.41 16757.03,-25324.59 16755.18,-25326.71 16753.27,-25328.78 16751.3,-25330.8 16749.28,-25332.77 16747.21,-25334.68 16745.09,-25336.53 16742.91,-25338.32 16740.69,-25340.05 16738.42,-25341.72 16736.1,-25343.32 16733.74,-25344.87 16731.34,-25346.34 16728.9,-25347.75 16726.42,-25349.09 16723.91,-25350.36 16721.36,-25351.57 16718.78,-25352.7 16716.17,-25353.76 16713.53,-25354.75 16710.87,-25355.66 16708.18,-25356.5 16705.47,-25357.27 16702.73,-25357.96 16699.98,-25358.58 16697.22,-25359.12 16694.44,-25359.58 16691.65,-25359.97 16688.85,-25360.28 16686.04,-25360.51 16683.23,-25360.67 16680.41,-25360.74 16679,-25360.75 16679,-25356.25 16680.35,-25356.24 16683.04,-25356.17 16685.73,-25356.02 16688.41,-25355.8 16691.09,-25355.5 16693.76,-25355.13 16696.42,-25354.69 16699.06,-25354.17 16701.69,-25353.58 16704.3,-25352.92 16706.89,-25352.19 16709.46,-25351.39 16712.01,-25350.51 16714.53,-25349.57 16717.03,-25348.55 16719.5,-25347.47 16721.93,-25346.32 16724.34,-25345.1 16726.71,-25343.82 16729.04,-25342.48 16731.33,-25341.06 16733.59,-25339.59 16735.8,-25338.06 16737.97,-25336.46 16740.1,-25334.81 16742.18,-25333.09 16744.21,-25331.32 16746.19,-25329.5 16748.12,-25327.62 16750,-25325.69 16751.82,-25323.71 16753.59,-25321.68 16755.31,-25319.6 16756.96,-25317.47 16758.56,-25315.3 16760.09,-25313.09 16761.56,-25310.83 16762.98,-25308.54 16764.32,-25306.21 16765.6,-25303.84 16766.82,-25301.43 16767.97,-25299 16769.05,-25296.53 16770.07,-25294.03 16771.01,-25291.51 16771.89,-25288.96 16772.69,-25286.39 16773.42,-25283.8 16774.08,-25281.19 16774.67,-25278.56 16775.19,-25275.92 16775.63,-25273.26 16776,-25270.59 16776.3,-25267.91 16776.52,-25265.23 16776.67,-25262.54 16776.74,-25259.85"/>
<polygon id="000002B6365DC220" class="l275d0" points="16776.75,-25258.5 16781.25,-25258.5 16781.24,-25259.91 16781.17,-25262.73 16781.01,-25265.54 16780.78,-25268.35 16780.47,-25271.15 16780.08,-25273.94 16779.62,-25276.72 16779.08,-25279.48 16778.46,-25282.23 16777.77,-25284.97 16777,-25287.68 16776.16,-25290.37 16775.25,-25293.03 16774.26,-25295.67 16773.2,-25298.28 16772.07,-25300.86 16770.86,-25303.41 16769.59,-25305.92 16768.25,-25308.4 16766.84,-25310.84 16765.37,-25313.24 16763.82,-25315.6 16762.22,-25317.92 16760.55,-25320.19 16758.82,-25322.41 16757.03,-25324.59 16755.18,-25326.71 16753.27,-25328.78 16751.3,-25330.8 16749.28,-25332.77 16747.21,-25334.68 16745.09,-25336.53 16742.91,-25338.32 16740.69,-25340.05 16738.42,-25341.72 16736.1,-25343.32 16733.74,-25344.87 16731.34,-25346.34 16728.9,-25347.75 16726.42,-25349.09 16723.91,-25350.36 16721.36,-25351.57 16718.78,-25352.7 16716.17,-25353.76 16713.53,-25354.75 16710.87,-25355.66 16708.18,-25356.5 16705.47,-25357.27 16702.73,-25357.96 16699.98,-25358.58 16697.22,-25359.12 16694.44,-25359.58 16691.65,-25359.97 16688.85,-25360.28 16686.04,-25360.51 16683.23,-25360.67 16680.41,-25360.74 16679,-25360.75 16679,-25356.25 16680.35,-25356.24 16683.04,-25356.17 16685.73,-25356.02 16688.41,-25355.8 16691.09,-25355.5 16693.76,-25355.13 16696.42,-25354.69 16699.06,-25354.17 16701.69,-25353.58 16704.3,-25352.92 16706.89,-25352.19 16709.46,-25351.39 16712.01,-25350.51 16714.53,-25349.57 16717.03,-25348.55 16719.5,-25347.47 16721.93,-25346.32 16724.34,-25345.1 16726.71,-25343.82 16729.04,-25342.48 16731.33,-25341.06 16733.59,-25339.59 16735.8,-25338.06 16737.97,-25336.46 16740.1,-25334.81 16742.18,-25333.09 16744.21,-25331.32 16746.19,-25329.5 16748.12,-25327.62 16750,-25325.69 16751.82,-25323.71 16753.59,-25321.68 16755.31,-25319.6 16756.96,-25317.47 16758.56,-25315.3 16760.09,-25313.09 16761.56,-25310.83 16762.98,-25308.54 16764.32,-25306.21 16765.6,-25303.84 16766.82,-25301.43 16767.97,-25299 16769.05,-25296.53 16770.07,-25294.03 16771.01,-25291.51 16771.89,-25288.96 16772.69,-25286.39 16773.42,-25283.8 16774.08,-25281.19 16774.67,-25278.56 16775.19,-25275.92 16775.63,-25273.26 16776,-25270.59 16776.3,-25267.91 16776.52,-25265.23 16776.67,-25262.54 16776.74,-25259.85"/>
<polygon id="000002B6365DC4C0" class="l275d0" points="16776.75,-25258.5 16781.25,-25258.5 16781.24,-25259.91 16781.17,-25262.73 16781.01,-25265.54 16780.78,-25268.35 16780.47,-25271.15 16780.08,-25273.94 16779.62,-25276.72 16779.08,-25279.48 16778.46,-25282.23 16777.77,-25284.97 16777,-25287.68 16776.16,-25290.37 16775.25,-25293.03 16774.26,-25295.67 16773.2,-25298.28 16772.07,-25300.86 16770.86,-25303.41 16769.59,-25305.92 16768.25,-25308.4 16766.84,-25310.84 16765.37,-25313.24 16763.82,-25315.6 16762.22,-25317.92 16760.55,-25320.19 16758.82,-25322.41 16757.03,-25324.59 16755.18,-25326.71 16753.27,-25328.78 16751.3,-25330.8 16749.28,-25332.77 16747.21,-25334.68 16745.09,-25336.53 16742.91,-25338.32 16740.69,-25340.05 16738.42,-25341.72 16736.1,-25343.32 16733.74,-25344.87 16731.34,-25346.34 16728.9,-25347.75 16726.42,-25349.09 16723.91,-25350.36 16721.36,-25351.57 16718.78,-25352.7 16716.17,-25353.76 16713.53,-25354.75 16710.87,-25355.66 16708.18,-25356.5 16705.47,-25357.27 16702.73,-25357.96 16699.98,-25358.58 16697.22,-25359.12 16694.44,-25359.58 16691.65,-25359.97 16688.85,-25360.28 16686.04,-25360.51 16683.23,-25360.67 16680.41,-25360.74 16679,-25360.75 16679,-25356.25 16680.35,-25356.24 16683.04,-25356.17 16685.73,-25356.02 16688.41,-25355.8 16691.09,-25355.5 16693.76,-25355.13 16696.42,-25354.69 16699.06,-25354.17 16701.69,-25353.58 16704.3,-25352.92 16706.89,-25352.19 16709.46,-25351.39 16712.01,-25350.51 16714.53,-25349.57 16717.03,-25348.55 16719.5,-25347.47 16721.93,-25346.32 16724.34,-25345.1 16726.71,-25343.82 16729.04,-25342.48 16731.33,-25341.06 16733.59,-25339.59 16735.8,-25338.06 16737.97,-25336.46 16740.1,-25334.81 16742.18,-25333.09 16744.21,-25331.32 16746.19,-25329.5 16748.12,-25327.62 16750,-25325.69 16751.82,-25323.71 16753.59,-25321.68 16755.31,-25319.6 16756.96,-25317.47 16758.56,-25315.3 16760.09,-25313.09 16761.56,-25310.83 16762.98,-25308.54 16764.32,-25306.21 16765.6,-25303.84 16766.82,-25301.43 16767.97,-25299 16769.05,-25296.53 16770.07,-25294.03 16771.01,-25291.51 16771.89,-25288.96 16772.69,-25286.39 16773.42,-25283.8 16774.08,-25281.19 16774.67,-25278.56 16775.19,-25275.92 16775.63,-25273.26 16776,-25270.59 16776.3,-25267.91 16776.52,-25265.23 16776.67,-25262.54 16776.74,-25259.85"/>
<polygon id="000002B6365DCBC0" class="l275d0" points="16776.75,-25258.5 16781.25,-25258.5 16781.24,-25259.91 16781.17,-25262.73 16781.01,-25265.54 16780.78,-25268.35 16780.47,-25271.15 16780.08,-25273.94 16779.62,-25276.72 16779.08,-25279.48 16778.46,-25282.23 16777.77,-25284.97 16777,-25287.68 16776.16,-25290.37 16775.25,-25293.03 16774.26,-25295.67 16773.2,-25298.28 16772.07,-25300.86 16770.86,-25303.41 16769.59,-25305.92 16768.25,-25308.4 16766.84,-25310.84 16765.37,-25313.24 16763.82,-25315.6 16762.22,-25317.92 16760.55,-25320.19 16758.82,-25322.41 16757.03,-25324.59 16755.18,-25326.71 16753.27,-25328.78 16751.3,-25330.8 16749.28,-25332.77 16747.21,-25334.68 16745.09,-25336.53 16742.91,-25338.32 16740.69,-25340.05 16738.42,-25341.72 16736.1,-25343.32 16733.74,-25344.87 16731.34,-25346.34 16728.9,-25347.75 16726.42,-25349.09 16723.91,-25350.36 16721.36,-25351.57 16718.78,-25352.7 16716.17,-25353.76 16713.53,-25354.75 16710.87,-25355.66 16708.18,-25356.5 16705.47,-25357.27 16702.73,-25357.96 16699.98,-25358.58 16697.22,-25359.12 16694.44,-25359.58 16691.65,-25359.97 16688.85,-25360.28 16686.04,-25360.51 16683.23,-25360.67 16680.41,-25360.74 16679,-25360.75 16679,-25356.25 16680.35,-25356.24 16683.04,-25356.17 16685.73,-25356.02 16688.41,-25355.8 16691.09,-25355.5 16693.76,-25355.13 16696.42,-25354.69 16699.06,-25354.17 16701.69,-25353.58 16704.3,-25352.92 16706.89,-25352.19 16709.46,-25351.39 16712.01,-25350.51 16714.53,-25349.57 16717.03,-25348.55 16719.5,-25347.47 16721.93,-25346.32 16724.34,-25345.1 16726.71,-25343.82 16729.04,-25342.48 16731.33,-25341.06 16733.59,-25339.59 16735.8,-25338.06 16737.97,-25336.46 16740.1,-25334.81 16742.18,-25333.09 16744.21,-25331.32 16746.19,-25329.5 16748.12,-25327.62 16750,-25325.69 16751.82,-25323.71 16753.59,-25321.68 16755.31,-25319.6 16756.96,-25317.47 16758.56,-25315.3 16760.09,-25313.09 16761.56,-25310.83 16762.98,-25308.54 16764.32,-25306.21 16765.6,-25303.84 16766.82,-25301.43 16767.97,-25299 16769.05,-25296.53 16770.07,-25294.03 16771.01,-25291.51 16771.89,-25288.96 16772.69,-25286.39 16773.42,-25283.8 16774.08,-25281.19 16774.67,-25278.56 16775.19,-25275.92 16775.63,-25273.26 16776,-25270.59 16776.3,-25267.91 16776.52,-25265.23 16776.67,-25262.54 16776.74,-25259.85"/>
<polygon id="000002B6365DCED0" class="l101d251" points="16736.75,-25258.5 16821.25,-25258.5 16821.24,-25260.17 16821.17,-25263.5 16821.01,-25266.83 16820.77,-25270.16 16820.46,-25273.48 16820.07,-25276.79 16819.6,-25280.09 16819.06,-25283.38 16818.44,-25286.66 16817.74,-25289.92 16816.96,-25293.17 16816.11,-25296.39 16815.19,-25299.59 16814.19,-25302.78 16813.11,-25305.93 16811.96,-25309.06 16810.74,-25312.17 16809.45,-25315.24 16808.08,-25318.28 16806.64,-25321.29 16805.14,-25324.27 16803.56,-25327.21 16801.92,-25330.11 16800.2,-25332.97 16798.42,-25335.79 16796.58,-25338.57 16794.67,-25341.3 16792.7,-25343.99 16790.66,-25346.63 16788.57,-25349.23 16786.41,-25351.77 16784.19,-25354.26 16781.92,-25356.7 16779.59,-25359.09 16777.2,-25361.42 16774.76,-25363.69 16772.27,-25365.91 16769.73,-25368.07 16767.13,-25370.16 16764.49,-25372.2 16761.8,-25374.17 16759.07,-25376.08 16756.29,-25377.92 16753.47,-25379.7 16750.61,-25381.42 16747.71,-25383.06 16744.77,-25384.64 16741.79,-25386.14 16738.78,-25387.58 16735.74,-25388.95 16732.67,-25390.24 16729.56,-25391.46 16726.43,-25392.61 16723.28,-25393.69 16720.09,-25394.69 16716.89,-25395.61 16713.67,-25396.46 16710.42,-25397.24 16707.16,-25397.94 16703.88,-25398.56 16700.59,-25399.1 16697.29,-25399.57 16693.98,-25399.96 16690.66,-25400.27 16687.33,-25400.51 16684,-25400.67 16680.67,-25400.74 16679,-25400.75 16679,-25316.25 16679.68,-25316.25 16681.03,-25316.22 16682.38,-25316.15 16683.73,-25316.06 16685.08,-25315.93 16686.43,-25315.77 16687.77,-25315.58 16689.1,-25315.36 16690.43,-25315.11 16691.76,-25314.82 16693.07,-25314.51 16694.38,-25314.16 16695.68,-25313.79 16696.97,-25313.38 16698.26,-25312.95 16699.53,-25312.48 16700.79,-25311.98 16702.04,-25311.46 16703.27,-25310.9 16704.49,-25310.32 16705.7,-25309.71 16706.89,-25309.07 16708.07,-25308.4 16709.23,-25307.71 16710.38,-25306.98 16711.51,-25306.23 16712.62,-25305.46 16713.71,-25304.66 16714.78,-25303.83 16715.83,-25302.98 16716.87,-25302.11 16717.88,-25301.21 16718.87,-25300.28 16719.84,-25299.34 16720.78,-25298.37 16721.71,-25297.38 16722.61,-25296.37 16723.48,-25295.33 16724.33,-25294.28 16725.16,-25293.21 16725.96,-25292.12 16726.73,-25291.01 16727.48,-25289.88 16728.21,-25288.73 16728.9,-25287.57 16729.57,-25286.39 16730.21,-25285.2 16730.82,-25283.99 16731.4,-25282.77 16731.96,-25281.54 16732.48,-25280.29 16732.98,-25279.03 16733.45,-25277.76 16733.88,-25276.47 16734.29,-25275.18 16734.66,-25273.88 16735.01,-25272.57 16735.32,-25271.26 16735.61,-25269.93 16735.86,-25268.6 16736.08,-25267.27 16736.27,-25265.93 16736.43,-25264.58 16736.56,-25263.23 16736.65,-25261.88 16736.72,-25260.53 16736.75,-25259.18"/>
<polygon id="000002B6365DC920" class="l101d251" points="16736.75,-25258.5 16821.25,-25258.5 16821.24,-25260.17 16821.17,-25263.5 16821.01,-25266.83 16820.77,-25270.16 16820.46,-25273.48 16820.07,-25276.79 16819.6,-25280.09 16819.06,-25283.38 16818.44,-25286.66 16817.74,-25289.92 16816.96,-25293.17 16816.11,-25296.39 16815.19,-25299.59 16814.19,-25302.78 16813.11,-25305.93 16811.96,-25309.06 16810.74,-25312.17 16809.45,-25315.24 16808.08,-25318.28 16806.64,-25321.29 16805.14,-25324.27 16803.56,-25327.21 16801.92,-25330.11 16800.2,-25332.97 16798.42,-25335.79 16796.58,-25338.57 16794.67,-25341.3 16792.7,-25343.99 16790.66,-25346.63 16788.57,-25349.23 16786.41,-25351.77 16784.19,-25354.26 16781.92,-25356.7 16779.59,-25359.09 16777.2,-25361.42 16774.76,-25363.69 16772.27,-25365.91 16769.73,-25368.07 16767.13,-25370.16 16764.49,-25372.2 16761.8,-25374.17 16759.07,-25376.08 16756.29,-25377.92 16753.47,-25379.7 16750.61,-25381.42 16747.71,-25383.06 16744.77,-25384.64 16741.79,-25386.14 16738.78,-25387.58 16735.74,-25388.95 16732.67,-25390.24 16729.56,-25391.46 16726.43,-25392.61 16723.28,-25393.69 16720.09,-25394.69 16716.89,-25395.61 16713.67,-25396.46 16710.42,-25397.24 16707.16,-25397.94 16703.88,-25398.56 16700.59,-25399.1 16697.29,-25399.57 16693.98,-25399.96 16690.66,-25400.27 16687.33,-25400.51 16684,-25400.67 16680.67,-25400.74 16679,-25400.75 16679,-25316.25 16679.68,-25316.25 16681.03,-25316.22 16682.38,-25316.15 16683.73,-25316.06 16685.08,-25315.93 16686.43,-25315.77 16687.77,-25315.58 16689.1,-25315.36 16690.43,-25315.11 16691.76,-25314.82 16693.07,-25314.51 16694.38,-25314.16 16695.68,-25313.79 16696.97,-25313.38 16698.26,-25312.95 16699.53,-25312.48 16700.79,-25311.98 16702.04,-25311.46 16703.27,-25310.9 16704.49,-25310.32 16705.7,-25309.71 16706.89,-25309.07 16708.07,-25308.4 16709.23,-25307.71 16710.38,-25306.98 16711.51,-25306.23 16712.62,-25305.46 16713.71,-25304.66 16714.78,-25303.83 16715.83,-25302.98 16716.87,-25302.11 16717.88,-25301.21 16718.87,-25300.28 16719.84,-25299.34 16720.78,-25298.37 16721.71,-25297.38 16722.61,-25296.37 16723.48,-25295.33 16724.33,-25294.28 16725.16,-25293.21 16725.96,-25292.12 16726.73,-25291.01 16727.48,-25289.88 16728.21,-25288.73 16728.9,-25287.57 16729.57,-25286.39 16730.21,-25285.2 16730.82,-25283.99 16731.4,-25282.77 16731.96,-25281.54 16732.48,-25280.29 16732.98,-25279.03 16733.45,-25277.76 16733.88,-25276.47 16734.29,-25275.18 16734.66,-25273.88 16735.01,-25272.57 16735.32,-25271.26 16735.61,-25269.93 16735.86,-25268.6 16736.08,-25267.27 16736.27,-25265.93 16736.43,-25264.58 16736.56,-25263.23 16736.65,-25261.88 16736.72,-25260.53 16736.75,-25259.18"/>
<polygon id="000002B6365DC840" class="l101d251" points="16736.75,-25258.5 16821.25,-25258.5 16821.24,-25260.17 16821.17,-25263.5 16821.01,-25266.83 16820.77,-25270.16 16820.46,-25273.48 16820.07,-25276.79 16819.6,-25280.09 16819.06,-25283.38 16818.44,-25286.66 16817.74,-25289.92 16816.96,-25293.17 16816.11,-25296.39 16815.19,-25299.59 16814.19,-25302.78 16813.11,-25305.93 16811.96,-25309.06 16810.74,-25312.17 16809.45,-25315.24 16808.08,-25318.28 16806.64,-25321.29 16805.14,-25324.27 16803.56,-25327.21 16801.92,-25330.11 16800.2,-25332.97 16798.42,-25335.79 16796.58,-25338.57 16794.67,-25341.3 16792.7,-25343.99 16790.66,-25346.63 16788.57,-25349.23 16786.41,-25351.77 16784.19,-25354.26 16781.92,-25356.7 16779.59,-25359.09 16777.2,-25361.42 16774.76,-25363.69 16772.27,-25365.91 16769.73,-25368.07 16767.13,-25370.16 16764.49,-25372.2 16761.8,-25374.17 16759.07,-25376.08 16756.29,-25377.92 16753.47,-25379.7 16750.61,-25381.42 16747.71,-25383.06 16744.77,-25384.64 16741.79,-25386.14 16738.78,-25387.58 16735.74,-25388.95 16732.67,-25390.24 16729.56,-25391.46 16726.43,-25392.61 16723.28,-25393.69 16720.09,-25394.69 16716.89,-25395.61 16713.67,-25396.46 16710.42,-25397.24 16707.16,-25397.94 16703.88,-25398.56 16700.59,-25399.1 16697.29,-25399.57 16693.98,-25399.96 16690.66,-25400.27 16687.33,-25400.51 16684,-25400.67 16680.67,-25400.74 16679,-25400.75 16679,-25316.25 16679.68,-25316.25 16681.03,-25316.22 16682.38,-25316.15 16683.73,-25316.06 16685.08,-25315.93 16686.43,-25315.77 16687.77,-25315.58 16689.1,-25315.36 16690.43,-25315.11 16691.76,-25314.82 16693.07,-25314.51 16694.38,-25314.16 16695.68,-25313.79 16696.97,-25313.38 16698.26,-25312.95 16699.53,-25312.48 16700.79,-25311.98 16702.04,-25311.46 16703.27,-25310.9 16704.49,-25310.32 16705.7,-25309.71 16706.89,-25309.07 16708.07,-25308.4 16709.23,-25307.71 16710.38,-25306.98 16711.51,-25306.23 16712.62,-25305.46 16713.71,-25304.66 16714.78,-25303.83 16715.83,-25302.98 16716.87,-25302.11 16717.88,-25301.21 16718.87,-25300.28 16719.84,-25299.34 16720.78,-25298.37 16721.71,-25297.38 16722.61,-25296.37 16723.48,-25295.33 16724.33,-25294.28 16725.16,-25293.21 16725.96,-25292.12 16726.73,-25291.01 16727.48,-25289.88 16728.21,-25288.73 16728.9,-25287.57 16729.57,-25286.39 16730.21,-25285.2 16730.82,-25283.99 16731.4,-25282.77 16731.96,-25281.54 16732.48,-25280.29 16732.98,-25279.03 16733.45,-25277.76 16733.88,-25276.47 16734.29,-25275.18 16734.66,-25273.88 16735.01,-25272.57 16735.32,-25271.26 16735.61,-25269.93 16735.86,-25268.6 16736.08,-25267.27 16736.27,-25265.93 16736.43,-25264.58 16736.56,-25263.23 16736.65,-25261.88 16736.72,-25260.53 16736.75,-25259.18"/>
<polygon id="000002B6365DCC30" class="l101d251" points="16736.75,-25258.5 16821.25,-25258.5 16821.24,-25260.17 16821.17,-25263.5 16821.01,-25266.83 16820.77,-25270.16 16820.46,-25273.48 16820.07,-25276.79 16819.6,-25280.09 16819.06,-25283.38 16818.44,-25286.66 16817.74,-25289.92 16816.96,-25293.17 16816.11,-25296.39 16815.19,-25299.59 16814.19,-25302.78 16813.11,-25305.93 16811.96,-25309.06 16810.74,-25312.17 16809.45,-25315.24 16808.08,-25318.28 16806.64,-25321.29 16805.14,-25324.27 16803.56,-25327.21 16801.92,-25330.11 16800.2,-25332.97 16798.42,-25335.79 16796.58,-25338.57 16794.67,-25341.3 16792.7,-25343.99 16790.66,-25346.63 16788.57,-25349.23 16786.41,-25351.77 16784.19,-25354.26 16781.92,-25356.7 16779.59,-25359.09 16777.2,-25361.42 16774.76,-25363.69 16772.27,-25365.91 16769.73,-25368.07 16767.13,-25370.16 16764.49,-25372.2 16761.8,-25374.17 16759.07,-25376.08 16756.29,-25377.92 16753.47,-25379.7 16750.61,-25381.42 16747.71,-25383.06 16744.77,-25384.64 16741.79,-25386.14 16738.78,-25387.58 16735.74,-25388.95 16732.67,-25390.24 16729.56,-25391.46 16726.43,-25392.61 16723.28,-25393.69 16720.09,-25394.69 16716.89,-25395.61 16713.67,-25396.46 16710.42,-25397.24 16707.16,-25397.94 16703.88,-25398.56 16700.59,-25399.1 16697.29,-25399.57 16693.98,-25399.96 16690.66,-25400.27 16687.33,-25400.51 16684,-25400.67 16680.67,-25400.74 16679,-25400.75 16679,-25316.25 16679.68,-25316.25 16681.03,-25316.22 16682.38,-25316.15 16683.73,-25316.06 16685.08,-25315.93 16686.43,-25315.77 16687.77,-25315.58 16689.1,-25315.36 16690.43,-25315.11 16691.76,-25314.82 16693.07,-25314.51 16694.38,-25314.16 16695.68,-25313.79 16696.97,-25313.38 16698.26,-25312.95 16699.53,-25312.48 16700.79,-25311.98 16702.04,-25311.46 16703.27,-25310.9 16704.49,-25310.32 16705.7,-25309.71 16706.89,-25309.07 16708.07,-25308.4 16709.23,-25307.71 16710.38,-25306.98 16711.51,-25306.23 16712.62,-25305.46 16713.71,-25304.66 16714.78,-25303.83 16715.83,-25302.98 16716.87,-25302.11 16717.88,-25301.21 16718.87,-25300.28 16719.84,-25299.34 16720.78,-25298.37 16721.71,-25297.38 16722.61,-25296.37 16723.48,-25295.33 16724.33,-25294.28 16725.16,-25293.21 16725.96,-25292.12 16726.73,-25291.01 16727.48,-25289.88 16728.21,-25288.73 16728.9,-25287.57 16729.57,-25286.39 16730.21,-25285.2 16730.82,-25283.99 16731.4,-25282.77 16731.96,-25281.54 16732.48,-25280.29 16732.98,-25279.03 16733.45,-25277.76 16733.88,-25276.47 16734.29,-25275.18 16734.66,-25273.88 16735.01,-25272.57 16735.32,-25271.26 16735.61,-25269.93 16735.86,-25268.6 16736.08,-25267.27 16736.27,-25265.93 16736.43,-25264.58 16736.56,-25263.23 16736.65,-25261.88 16736.72,-25260.53 16736.75,-25259.18"/>
<polygon id="000002B6365DCD10" class="l101d251" points="16736.75,-25258.5 16821.25,-25258.5 16821.24,-25260.17 16821.17,-25263.5 16821.01,-25266.83 16820.77,-25270.16 16820.46,-25273.48 16820.07,-25276.79 16819.6,-25280.09 16819.06,-25283.38 16818.44,-25286.66 16817.74,-25289.92 16816.96,-25293.17 16816.11,-25296.39 16815.19,-25299.59 16814.19,-25302.78 16813.11,-25305.93 16811.96,-25309.06 16810.74,-25312.17 16809.45,-25315.24 16808.08,-25318.28 16806.64,-25321.29 16805.14,-25324.27 16803.56,-25327.21 16801.92,-25330.11 16800.2,-25332.97 16798.42,-25335.79 16796.58,-25338.57 16794.67,-25341.3 16792.7,-25343.99 16790.66,-25346.63 16788.57,-25349.23 16786.41,-25351.77 16784.19,-25354.26 16781.92,-25356.7 16779.59,-25359.09 16777.2,-25361.42 16774.76,-25363.69 16772.27,-25365.91 16769.73,-25368.07 16767.13,-25370.16 16764.49,-25372.2 16761.8,-25374.17 16759.07,-25376.08 16756.29,-25377.92 16753.47,-25379.7 16750.61,-25381.42 16747.71,-25383.06 16744.77,-25384.64 16741.79,-25386.14 16738.78,-25387.58 16735.74,-25388.95 16732.67,-25390.24 16729.56,-25391.46 16726.43,-25392.61 16723.28,-25393.69 16720.09,-25394.69 16716.89,-25395.61 16713.67,-25396.46 16710.42,-25397.24 16707.16,-25397.94 16703.88,-25398.56 16700.59,-25399.1 16697.29,-25399.57 16693.98,-25399.96 16690.66,-25400.27 16687.33,-25400.51 16684,-25400.67 16680.67,-25400.74 16679,-25400.75 16679,-25316.25 16679.68,-25316.25 16681.03,-25316.22 16682.38,-25316.15 16683.73,-25316.06 16685.08,-25315.93 16686.43,-25315.77 16687.77,-25315.58 16689.1,-25315.36 16690.43,-25315.11 16691.76,-25314.82 16693.07,-25314.51 16694.38,-25314.16 16695.68,-25313.79 16696.97,-25313.38 16698.26,-25312.95 16699.53,-25312.48 16700.79,-25311.98 16702.04,-25311.46 16703.27,-25310.9 16704.49,-25310.32 16705.7,-25309.71 16706.89,-25309.07 16708.07,-25308.4 16709.23,-25307.71 16710.38,-25306.98 16711.51,-25306.23 16712.62,-25305.46 16713.71,-25304.66 16714.78,-25303.83 16715.83,-25302.98 16716.87,-25302.11 16717.88,-25301.21 16718.87,-25300.28 16719.84,-25299.34 16720.78,-25298.37 16721.71,-25297.38 16722.61,-25296.37 16723.48,-25295.33 16724.33,-25294.28 16725.16,-25293.21 16725.96,-25292.12 16726.73,-25291.01 16727.48,-25289.88 16728.21,-25288.73 16728.9,-25287.57 16729.57,-25286.39 16730.21,-25285.2 16730.82,-25283.99 16731.4,-25282.77 16731.96,-25281.54 16732.48,-25280.29 16732.98,-25279.03 16733.45,-25277.76 16733.88,-25276.47 16734.29,-25275.18 16734.66,-25273.88 16735.01,-25272.57 16735.32,-25271.26 16735.61,-25269.93 16735.86,-25268.6 16736.08,-25267.27 16736.27,-25265.93 16736.43,-25264.58 16736.56,-25263.23 16736.65,-25261.88 16736.72,-25260.53 16736.75,-25259.18"/>
<polygon id="000002B6365DC530" class="l101d251" points="16736.75,-25258.5 16821.25,-25258.5 16821.24,-25260.17 16821.17,-25263.5 16821.01,-25266.83 16820.77,-25270.16 16820.46,-25273.48 16820.07,-25276.79 16819.6,-25280.09 16819.06,-25283.38 16818.44,-25286.66 16817.74,-25289.92 16816.96,-25293.17 16816.11,-25296.39 16815.19,-25299.59 16814.19,-25302.78 16813.11,-25305.93 16811.96,-25309.06 16810.74,-25312.17 16809.45,-25315.24 16808.08,-25318.28 16806.64,-25321.29 16805.14,-25324.27 16803.56,-25327.21 16801.92,-25330.11 16800.2,-25332.97 16798.42,-25335.79 16796.58,-25338.57 16794.67,-25341.3 16792.7,-25343.99 16790.66,-25346.63 16788.57,-25349.23 16786.41,-25351.77 16784.19,-25354.26 16781.92,-25356.7 16779.59,-25359.09 16777.2,-25361.42 16774.76,-25363.69 16772.27,-25365.91 16769.73,-25368.07 16767.13,-25370.16 16764.49,-25372.2 16761.8,-25374.17 16759.07,-25376.08 16756.29,-25377.92 16753.47,-25379.7 16750.61,-25381.42 16747.71,-25383.06 16744.77,-25384.64 16741.79,-25386.14 16738.78,-25387.58 16735.74,-25388.95 16732.67,-25390.24 16729.56,-25391.46 16726.43,-25392.61 16723.28,-25393.69 16720.09,-25394.69 16716.89,-25395.61 16713.67,-25396.46 16710.42,-25397.24 16707.16,-25397.94 16703.88,-25398.56 16700.59,-25399.1 16697.29,-25399.57 16693.98,-25399.96 16690.66,-25400.27 16687.33,-25400.51 16684,-25400.67 16680.67,-25400.74 16679,-25400.75 16679,-25316.25 16679.68,-25316.25 16681.03,-25316.22 16682.38,-25316.15 16683.73,-25316.06 16685.08,-25315.93 16686.43,-25315.77 16687.77,-25315.58 16689.1,-25315.36 16690.43,-25315.11 16691.76,-25314.82 16693.07,-25314.51 16694.38,-25314.16 16695.68,-25313.79 16696.97,-25313.38 16698.26,-25312.95 16699.53,-25312.48 16700.79,-25311.98 16702.04,-25311.46 16703.27,-25310.9 16704.49,-25310.32 16705.7,-25309.71 16706.89,-25309.07 16708.07,-25308.4 16709.23,-25307.71 16710.38,-25306.98 16711.51,-25306.23 16712.62,-25305.46 16713.71,-25304.66 16714.78,-25303.83 16715.83,-25302.98 16716.87,-25302.11 16717.88,-25301.21 16718.87,-25300.28 16719.84,-25299.34 16720.78,-25298.37 16721.71,-25297.38 16722.61,-25296.37 16723.48,-25295.33 16724.33,-25294.28 16725.16,-25293.21 16725.96,-25292.12 16726.73,-25291.01 16727.48,-25289.88 16728.21,-25288.73 16728.9,-25287.57 16729.57,-25286.39 16730.21,-25285.2 16730.82,-25283.99 16731.4,-25282.77 16731.96,-25281.54 16732.48,-25280.29 16732.98,-25279.03 16733.45,-25277.76 16733.88,-25276.47 16734.29,-25275.18 16734.66,-25273.88 16735.01,-25272.57 16735.32,-25271.26 16735.61,-25269.93 16735.86,-25268.6 16736.08,-25267.27 16736.27,-25265.93 16736.43,-25264.58 16736.56,-25263.23 16736.65,-25261.88 16736.72,-25260.53 16736.75,-25259.18"/>
<polygon id="000002B6365DC5A0" class="l275d0" points="16781.25,-23038 16781.25,-25258.5 16776.75,-25258.5 16776.75,-23038"/>
<polygon id="000002B6365DC990" class="l275d0" points="16781.25,-23038 16781.25,-25258.5 16776.75,-25258.5 16776.75,-23038"/>
<polygon id="000002B6365DC7D0" class="l275d0" points="16781.25,-23038 16781.25,-25258.5 16776.75,-25258.5 16776.75,-23038"/>
<polygon id="000002B6365DCA00" class="l275d0" points="16781.25,-23038 16781.25,-25258.5 16776.75,-25258.5 16776.75,-23038"/>
<polygon id="000002B6365DCA70" class="l275d0" points="16781.25,-23038 16781.25,-25258.5 16776.75,-25258.5 16776.75,-23038"/>
<polygon id="000002B6365DCD80" class="l275d0" points="16781.25,-23038 16781.25,-25258.5 16776.75,-25258.5 16776.75,-23038"/>
<polygon id="000002B6365DCDF0" class="l101d251" points="16821.25,-22998 16821.25,-25298.5 16736.75,-25298.5 16736.75,-22998"/>
<polygon id="000002B6361D9040" class="l101d251" points="16821.25,-22998 16821.25,-25298.5 16736.75,-25298.5 16736.75,-22998"/>
<polygon id="000002B6361D92E0" class="l101d251" points="16821.25,-22998 16821.25,-25298.5 16736.75,-25298.5 16736.75,-22998"/>
<polygon id="000002B6361D9740" class="l101d251" points="16821.25,-22998 16821.25,-25298.5 16736.75,-25298.5 16736.75,-22998"/>
<polygon id="000002B6361D9190" class="l101d251" points="16821.25,-22998 16821.25,-25298.5 16736.75,-25298.5 16736.75,-22998"/>
<polygon id="000002B6361D8D30" class="l101d251" points="16821.25,-22998 16821.25,-25298.5 16736.75,-25298.5 16736.75,-22998"/>
<polygon id="000002B6361D8B00" class="l275d0" points="16879,-22935.75 16879,-22940.25 16877.65,-22940.26 16874.96,-22940.33 16872.27,-22940.48 16869.59,-22940.7 16866.91,-22941 16864.24,-22941.37 16861.58,-22941.81 16858.94,-22942.33 16856.31,-22942.92 16853.7,-22943.58 16851.11,-22944.31 16848.54,-22945.11 16845.99,-22945.99 16843.47,-22946.93 16840.97,-22947.95 16838.5,-22949.03 16836.07,-22950.18 16833.66,-22951.4 16831.29,-22952.68 16828.96,-22954.02 16826.67,-22955.44 16824.41,-22956.91 16822.2,-22958.44 16820.03,-22960.04 16817.9,-22961.69 16815.82,-22963.41 16813.79,-22965.18 16811.81,-22967 16809.88,-22968.88 16808,-22970.81 16806.18,-22972.79 16804.41,-22974.82 16802.69,-22976.9 16801.04,-22979.03 16799.44,-22981.2 16797.91,-22983.41 16796.44,-22985.67 16795.02,-22987.96 16793.68,-22990.29 16792.4,-22992.66 16791.18,-22995.07 16790.03,-22997.5 16788.95,-22999.97 16787.93,-23002.47 16786.99,-23004.99 16786.11,-23007.54 16785.31,-23010.11 16784.58,-23012.7 16783.92,-23015.31 16783.33,-23017.94 16782.81,-23020.58 16782.37,-23023.24 16782,-23025.91 16781.7,-23028.59 16781.48,-23031.27 16781.33,-23033.96 16781.26,-23036.65 16781.25,-23038 16776.75,-23038 16776.76,-23036.59 16776.83,-23033.77 16776.99,-23030.96 16777.22,-23028.15 16777.53,-23025.35 16777.92,-23022.56 16778.38,-23019.78 16778.92,-23017.02 16779.54,-23014.27 16780.23,-23011.53 16781,-23008.82 16781.84,-23006.13 16782.75,-23003.47 16783.74,-23000.83 16784.8,-22998.22 16785.93,-22995.64 16787.14,-22993.09 16788.41,-22990.58 16789.75,-22988.1 16791.16,-22985.66 16792.63,-22983.26 16794.18,-22980.9 16795.78,-22978.58 16797.45,-22976.31 16799.18,-22974.09 16800.97,-22971.91 16802.82,-22969.79 16804.73,-22967.72 16806.7,-22965.7 16808.72,-22963.73 16810.79,-22961.82 16812.91,-22959.97 16815.09,-22958.18 16817.31,-22956.45 16819.58,-22954.78 16821.9,-22953.18 16824.26,-22951.63 16826.66,-22950.16 16829.1,-22948.75 16831.58,-22947.41 16834.09,-22946.14 16836.64,-22944.93 16839.22,-22943.8 16841.83,-22942.74 16844.47,-22941.75 16847.13,-22940.84 16849.82,-22940 16852.53,-22939.23 16855.27,-22938.54 16858.02,-22937.92 16860.78,-22937.38 16863.56,-22936.92 16866.35,-22936.53 16869.15,-22936.22 16871.96,-22935.99 16874.77,-22935.83 16877.59,-22935.76"/>
<polygon id="000002B6361D8B70" class="l275d0" points="16879,-22935.75 16879,-22940.25 16877.65,-22940.26 16874.96,-22940.33 16872.27,-22940.48 16869.59,-22940.7 16866.91,-22941 16864.24,-22941.37 16861.58,-22941.81 16858.94,-22942.33 16856.31,-22942.92 16853.7,-22943.58 16851.11,-22944.31 16848.54,-22945.11 16845.99,-22945.99 16843.47,-22946.93 16840.97,-22947.95 16838.5,-22949.03 16836.07,-22950.18 16833.66,-22951.4 16831.29,-22952.68 16828.96,-22954.02 16826.67,-22955.44 16824.41,-22956.91 16822.2,-22958.44 16820.03,-22960.04 16817.9,-22961.69 16815.82,-22963.41 16813.79,-22965.18 16811.81,-22967 16809.88,-22968.88 16808,-22970.81 16806.18,-22972.79 16804.41,-22974.82 16802.69,-22976.9 16801.04,-22979.03 16799.44,-22981.2 16797.91,-22983.41 16796.44,-22985.67 16795.02,-22987.96 16793.68,-22990.29 16792.4,-22992.66 16791.18,-22995.07 16790.03,-22997.5 16788.95,-22999.97 16787.93,-23002.47 16786.99,-23004.99 16786.11,-23007.54 16785.31,-23010.11 16784.58,-23012.7 16783.92,-23015.31 16783.33,-23017.94 16782.81,-23020.58 16782.37,-23023.24 16782,-23025.91 16781.7,-23028.59 16781.48,-23031.27 16781.33,-23033.96 16781.26,-23036.65 16781.25,-23038 16776.75,-23038 16776.76,-23036.59 16776.83,-23033.77 16776.99,-23030.96 16777.22,-23028.15 16777.53,-23025.35 16777.92,-23022.56 16778.38,-23019.78 16778.92,-23017.02 16779.54,-23014.27 16780.23,-23011.53 16781,-23008.82 16781.84,-23006.13 16782.75,-23003.47 16783.74,-23000.83 16784.8,-22998.22 16785.93,-22995.64 16787.14,-22993.09 16788.41,-22990.58 16789.75,-22988.1 16791.16,-22985.66 16792.63,-22983.26 16794.18,-22980.9 16795.78,-22978.58 16797.45,-22976.31 16799.18,-22974.09 16800.97,-22971.91 16802.82,-22969.79 16804.73,-22967.72 16806.7,-22965.7 16808.72,-22963.73 16810.79,-22961.82 16812.91,-22959.97 16815.09,-22958.18 16817.31,-22956.45 16819.58,-22954.78 16821.9,-22953.18 16824.26,-22951.63 16826.66,-22950.16 16829.1,-22948.75 16831.58,-22947.41 16834.09,-22946.14 16836.64,-22944.93 16839.22,-22943.8 16841.83,-22942.74 16844.47,-22941.75 16847.13,-22940.84 16849.82,-22940 16852.53,-22939.23 16855.27,-22938.54 16858.02,-22937.92 16860.78,-22937.38 16863.56,-22936.92 16866.35,-22936.53 16869.15,-22936.22 16871.96,-22935.99 16874.77,-22935.83 16877.59,-22935.76"/>
<polygon id="000002B6361D97B0" class="l275d0" points="16879,-22935.75 16879,-22940.25 16877.65,-22940.26 16874.96,-22940.33 16872.27,-22940.48 16869.59,-22940.7 16866.91,-22941 16864.24,-22941.37 16861.58,-22941.81 16858.94,-22942.33 16856.31,-22942.92 16853.7,-22943.58 16851.11,-22944.31 16848.54,-22945.11 16845.99,-22945.99 16843.47,-22946.93 16840.97,-22947.95 16838.5,-22949.03 16836.07,-22950.18 16833.66,-22951.4 16831.29,-22952.68 16828.96,-22954.02 16826.67,-22955.44 16824.41,-22956.91 16822.2,-22958.44 16820.03,-22960.04 16817.9,-22961.69 16815.82,-22963.41 16813.79,-22965.18 16811.81,-22967 16809.88,-22968.88 16808,-22970.81 16806.18,-22972.79 16804.41,-22974.82 16802.69,-22976.9 16801.04,-22979.03 16799.44,-22981.2 16797.91,-22983.41 16796.44,-22985.67 16795.02,-22987.96 16793.68,-22990.29 16792.4,-22992.66 16791.18,-22995.07 16790.03,-22997.5 16788.95,-22999.97 16787.93,-23002.47 16786.99,-23004.99 16786.11,-23007.54 16785.31,-23010.11 16784.58,-23012.7 16783.92,-23015.31 16783.33,-23017.94 16782.81,-23020.58 16782.37,-23023.24 16782,-23025.91 16781.7,-23028.59 16781.48,-23031.27 16781.33,-23033.96 16781.26,-23036.65 16781.25,-23038 16776.75,-23038 16776.76,-23036.59 16776.83,-23033.77 16776.99,-23030.96 16777.22,-23028.15 16777.53,-23025.35 16777.92,-23022.56 16778.38,-23019.78 16778.92,-23017.02 16779.54,-23014.27 16780.23,-23011.53 16781,-23008.82 16781.84,-23006.13 16782.75,-23003.47 16783.74,-23000.83 16784.8,-22998.22 16785.93,-22995.64 16787.14,-22993.09 16788.41,-22990.58 16789.75,-22988.1 16791.16,-22985.66 16792.63,-22983.26 16794.18,-22980.9 16795.78,-22978.58 16797.45,-22976.31 16799.18,-22974.09 16800.97,-22971.91 16802.82,-22969.79 16804.73,-22967.72 16806.7,-22965.7 16808.72,-22963.73 16810.79,-22961.82 16812.91,-22959.97 16815.09,-22958.18 16817.31,-22956.45 16819.58,-22954.78 16821.9,-22953.18 16824.26,-22951.63 16826.66,-22950.16 16829.1,-22948.75 16831.58,-22947.41 16834.09,-22946.14 16836.64,-22944.93 16839.22,-22943.8 16841.83,-22942.74 16844.47,-22941.75 16847.13,-22940.84 16849.82,-22940 16852.53,-22939.23 16855.27,-22938.54 16858.02,-22937.92 16860.78,-22937.38 16863.56,-22936.92 16866.35,-22936.53 16869.15,-22936.22 16871.96,-22935.99 16874.77,-22935.83 16877.59,-22935.76"/>
<polygon id="000002B6361D8BE0" class="l275d0" points="16879,-22935.75 16879,-22940.25 16877.65,-22940.26 16874.96,-22940.33 16872.27,-22940.48 16869.59,-22940.7 16866.91,-22941 16864.24,-22941.37 16861.58,-22941.81 16858.94,-22942.33 16856.31,-22942.92 16853.7,-22943.58 16851.11,-22944.31 16848.54,-22945.11 16845.99,-22945.99 16843.47,-22946.93 16840.97,-22947.95 16838.5,-22949.03 16836.07,-22950.18 16833.66,-22951.4 16831.29,-22952.68 16828.96,-22954.02 16826.67,-22955.44 16824.41,-22956.91 16822.2,-22958.44 16820.03,-22960.04 16817.9,-22961.69 16815.82,-22963.41 16813.79,-22965.18 16811.81,-22967 16809.88,-22968.88 16808,-22970.81 16806.18,-22972.79 16804.41,-22974.82 16802.69,-22976.9 16801.04,-22979.03 16799.44,-22981.2 16797.91,-22983.41 16796.44,-22985.67 16795.02,-22987.96 16793.68,-22990.29 16792.4,-22992.66 16791.18,-22995.07 16790.03,-22997.5 16788.95,-22999.97 16787.93,-23002.47 16786.99,-23004.99 16786.11,-23007.54 16785.31,-23010.11 16784.58,-23012.7 16783.92,-23015.31 16783.33,-23017.94 16782.81,-23020.58 16782.37,-23023.24 16782,-23025.91 16781.7,-23028.59 16781.48,-23031.27 16781.33,-23033.96 16781.26,-23036.65 16781.25,-23038 16776.75,-23038 16776.76,-23036.59 16776.83,-23033.77 16776.99,-23030.96 16777.22,-23028.15 16777.53,-23025.35 16777.92,-23022.56 16778.38,-23019.78 16778.92,-23017.02 16779.54,-23014.27 16780.23,-23011.53 16781,-23008.82 16781.84,-23006.13 16782.75,-23003.47 16783.74,-23000.83 16784.8,-22998.22 16785.93,-22995.64 16787.14,-22993.09 16788.41,-22990.58 16789.75,-22988.1 16791.16,-22985.66 16792.63,-22983.26 16794.18,-22980.9 16795.78,-22978.58 16797.45,-22976.31 16799.18,-22974.09 16800.97,-22971.91 16802.82,-22969.79 16804.73,-22967.72 16806.7,-22965.7 16808.72,-22963.73 16810.79,-22961.82 16812.91,-22959.97 16815.09,-22958.18 16817.31,-22956.45 16819.58,-22954.78 16821.9,-22953.18 16824.26,-22951.63 16826.66,-22950.16 16829.1,-22948.75 16831.58,-22947.41 16834.09,-22946.14 16836.64,-22944.93 16839.22,-22943.8 16841.83,-22942.74 16844.47,-22941.75 16847.13,-22940.84 16849.82,-22940 16852.53,-22939.23 16855.27,-22938.54 16858.02,-22937.92 16860.78,-22937.38 16863.56,-22936.92 16866.35,-22936.53 16869.15,-22936.22 16871.96,-22935.99 16874.77,-22935.83 16877.59,-22935.76"/>
<polygon id="000002B6361D9820" class="l275d0" points="16879,-22935.75 16879,-22940.25 16877.65,-22940.26 16874.96,-22940.33 16872.27,-22940.48 16869.59,-22940.7 16866.91,-22941 16864.24,-22941.37 16861.58,-22941.81 16858.94,-22942.33 16856.31,-22942.92 16853.7,-22943.58 16851.11,-22944.31 16848.54,-22945.11 16845.99,-22945.99 16843.47,-22946.93 16840.97,-22947.95 16838.5,-22949.03 16836.07,-22950.18 16833.66,-22951.4 16831.29,-22952.68 16828.96,-22954.02 16826.67,-22955.44 16824.41,-22956.91 16822.2,-22958.44 16820.03,-22960.04 16817.9,-22961.69 16815.82,-22963.41 16813.79,-22965.18 16811.81,-22967 16809.88,-22968.88 16808,-22970.81 16806.18,-22972.79 16804.41,-22974.82 16802.69,-22976.9 16801.04,-22979.03 16799.44,-22981.2 16797.91,-22983.41 16796.44,-22985.67 16795.02,-22987.96 16793.68,-22990.29 16792.4,-22992.66 16791.18,-22995.07 16790.03,-22997.5 16788.95,-22999.97 16787.93,-23002.47 16786.99,-23004.99 16786.11,-23007.54 16785.31,-23010.11 16784.58,-23012.7 16783.92,-23015.31 16783.33,-23017.94 16782.81,-23020.58 16782.37,-23023.24 16782,-23025.91 16781.7,-23028.59 16781.48,-23031.27 16781.33,-23033.96 16781.26,-23036.65 16781.25,-23038 16776.75,-23038 16776.76,-23036.59 16776.83,-23033.77 16776.99,-23030.96 16777.22,-23028.15 16777.53,-23025.35 16777.92,-23022.56 16778.38,-23019.78 16778.92,-23017.02 16779.54,-23014.27 16780.23,-23011.53 16781,-23008.82 16781.84,-23006.13 16782.75,-23003.47 16783.74,-23000.83 16784.8,-22998.22 16785.93,-22995.64 16787.14,-22993.09 16788.41,-22990.58 16789.75,-22988.1 16791.16,-22985.66 16792.63,-22983.26 16794.18,-22980.9 16795.78,-22978.58 16797.45,-22976.31 16799.18,-22974.09 16800.97,-22971.91 16802.82,-22969.79 16804.73,-22967.72 16806.7,-22965.7 16808.72,-22963.73 16810.79,-22961.82 16812.91,-22959.97 16815.09,-22958.18 16817.31,-22956.45 16819.58,-22954.78 16821.9,-22953.18 16824.26,-22951.63 16826.66,-22950.16 16829.1,-22948.75 16831.58,-22947.41 16834.09,-22946.14 16836.64,-22944.93 16839.22,-22943.8 16841.83,-22942.74 16844.47,-22941.75 16847.13,-22940.84 16849.82,-22940 16852.53,-22939.23 16855.27,-22938.54 16858.02,-22937.92 16860.78,-22937.38 16863.56,-22936.92 16866.35,-22936.53 16869.15,-22936.22 16871.96,-22935.99 16874.77,-22935.83 16877.59,-22935.76"/>
<polygon id="000002B6361D8C50" class="l275d0" points="16879,-22935.75 16879,-22940.25 16877.65,-22940.26 16874.96,-22940.33 16872.27,-22940.48 16869.59,-22940.7 16866.91,-22941 16864.24,-22941.37 16861.58,-22941.81 16858.94,-22942.33 16856.31,-22942.92 16853.7,-22943.58 16851.11,-22944.31 16848.54,-22945.11 16845.99,-22945.99 16843.47,-22946.93 16840.97,-22947.95 16838.5,-22949.03 16836.07,-22950.18 16833.66,-22951.4 16831.29,-22952.68 16828.96,-22954.02 16826.67,-22955.44 16824.41,-22956.91 16822.2,-22958.44 16820.03,-22960.04 16817.9,-22961.69 16815.82,-22963.41 16813.79,-22965.18 16811.81,-22967 16809.88,-22968.88 16808,-22970.81 16806.18,-22972.79 16804.41,-22974.82 16802.69,-22976.9 16801.04,-22979.03 16799.44,-22981.2 16797.91,-22983.41 16796.44,-22985.67 16795.02,-22987.96 16793.68,-22990.29 16792.4,-22992.66 16791.18,-22995.07 16790.03,-22997.5 16788.95,-22999.97 16787.93,-23002.47 16786.99,-23004.99 16786.11,-23007.54 16785.31,-23010.11 16784.58,-23012.7 16783.92,-23015.31 16783.33,-23017.94 16782.81,-23020.58 16782.37,-23023.24 16782,-23025.91 16781.7,-23028.59 16781.48,-23031.27 16781.33,-23033.96 16781.26,-23036.65 16781.25,-23038 16776.75,-23038 16776.76,-23036.59 16776.83,-23033.77 16776.99,-23030.96 16777.22,-23028.15 16777.53,-23025.35 16777.92,-23022.56 16778.38,-23019.78 16778.92,-23017.02 16779.54,-23014.27 16780.23,-23011.53 16781,-23008.82 16781.84,-23006.13 16782.75,-23003.47 16783.74,-23000.83 16784.8,-22998.22 16785.93,-22995.64 16787.14,-22993.09 16788.41,-22990.58 16789.75,-22988.1 16791.16,-22985.66 16792.63,-22983.26 16794.18,-22980.9 16795.78,-22978.58 16797.45,-22976.31 16799.18,-22974.09 16800.97,-22971.91 16802.82,-22969.79 16804.73,-22967.72 16806.7,-22965.7 16808.72,-22963.73 16810.79,-22961.82 16812.91,-22959.97 16815.09,-22958.18 16817.31,-22956.45 16819.58,-22954.78 16821.9,-22953.18 16824.26,-22951.63 16826.66,-22950.16 16829.1,-22948.75 16831.58,-22947.41 16834.09,-22946.14 16836.64,-22944.93 16839.22,-22943.8 16841.83,-22942.74 16844.47,-22941.75 16847.13,-22940.84 16849.82,-22940 16852.53,-22939.23 16855.27,-22938.54 16858.02,-22937.92 16860.78,-22937.38 16863.56,-22936.92 16866.35,-22936.53 16869.15,-22936.22 16871.96,-22935.99 16874.77,-22935.83 16877.59,-22935.76"/>
<polygon id="000002B6361D9350" class="l101d251" points="16879,-22895.75 16879,-22980.25 16878.32,-22980.25 16876.97,-22980.28 16875.62,-22980.35 16874.27,-22980.44 16872.92,-22980.57 16871.57,-22980.73 16870.23,-22980.92 16868.9,-22981.14 16867.57,-22981.39 16866.24,-22981.68 16864.93,-22981.99 16863.62,-22982.34 16862.32,-22982.71 16861.03,-22983.12 16859.74,-22983.55 16858.47,-22984.02 16857.21,-22984.52 16855.96,-22985.04 16854.73,-22985.6 16853.51,-22986.18 16852.3,-22986.79 16851.11,-22987.43 16849.93,-22988.1 16848.77,-22988.79 16847.62,-22989.52 16846.49,-22990.27 16845.38,-22991.04 16844.29,-22991.84 16843.22,-22992.67 16842.17,-22993.52 16841.13,-22994.39 16840.12,-22995.29 16839.13,-22996.22 16838.16,-22997.16 16837.22,-22998.13 16836.29,-22999.12 16835.39,-23000.13 16834.52,-23001.17 16833.67,-23002.22 16832.84,-23003.29 16832.04,-23004.38 16831.27,-23005.49 16830.52,-23006.62 16829.79,-23007.77 16829.1,-23008.93 16828.43,-23010.11 16827.79,-23011.3 16827.18,-23012.51 16826.6,-23013.73 16826.04,-23014.96 16825.52,-23016.21 16825.02,-23017.47 16824.55,-23018.74 16824.12,-23020.03 16823.71,-23021.32 16823.34,-23022.62 16822.99,-23023.93 16822.68,-23025.24 16822.39,-23026.57 16822.14,-23027.9 16821.92,-23029.23 16821.73,-23030.57 16821.57,-23031.92 16821.44,-23033.27 16821.35,-23034.62 16821.28,-23035.97 16821.25,-23037.32 16821.25,-23038 16736.75,-23038 16736.76,-23036.33 16736.83,-23033 16736.99,-23029.67 16737.23,-23026.34 16737.54,-23023.02 16737.93,-23019.71 16738.4,-23016.41 16738.94,-23013.12 16739.56,-23009.84 16740.26,-23006.58 16741.04,-23003.33 16741.89,-23000.11 16742.81,-22996.91 16743.81,-22993.72 16744.89,-22990.57 16746.04,-22987.44 16747.26,-22984.33 16748.55,-22981.26 16749.92,-22978.22 16751.36,-22975.21 16752.86,-22972.23 16754.44,-22969.29 16756.08,-22966.39 16757.8,-22963.53 16759.58,-22960.71 16761.42,-22957.93 16763.33,-22955.2 16765.3,-22952.51 16767.34,-22949.87 16769.43,-22947.27 16771.59,-22944.73 16773.81,-22942.24 16776.08,-22939.8 16778.41,-22937.41 16780.8,-22935.08 16783.24,-22932.81 16785.73,-22930.59 16788.27,-22928.43 16790.87,-22926.34 16793.51,-22924.3 16796.2,-22922.33 16798.93,-22920.42 16801.71,-22918.58 16804.53,-22916.8 16807.39,-22915.08 16810.29,-22913.44 16813.23,-22911.86 16816.21,-22910.36 16819.22,-22908.92 16822.26,-22907.55 16825.33,-22906.26 16828.44,-22905.04 16831.57,-22903.89 16834.72,-22902.81 16837.91,-22901.81 16841.11,-22900.89 16844.33,-22900.04 16847.58,-22899.26 16850.84,-22898.56 16854.12,-22897.94 16857.41,-22897.4 16860.71,-22896.93 16864.02,-22896.54 16867.34,-22896.23 16870.67,-22895.99 16874,-22895.83 16877.33,-22895.76"/>
<polygon id="000002B6361D8A20" class="l101d251" points="16879,-22895.75 16879,-22980.25 16878.32,-22980.25 16876.97,-22980.28 16875.62,-22980.35 16874.27,-22980.44 16872.92,-22980.57 16871.57,-22980.73 16870.23,-22980.92 16868.9,-22981.14 16867.57,-22981.39 16866.24,-22981.68 16864.93,-22981.99 16863.62,-22982.34 16862.32,-22982.71 16861.03,-22983.12 16859.74,-22983.55 16858.47,-22984.02 16857.21,-22984.52 16855.96,-22985.04 16854.73,-22985.6 16853.51,-22986.18 16852.3,-22986.79 16851.11,-22987.43 16849.93,-22988.1 16848.77,-22988.79 16847.62,-22989.52 16846.49,-22990.27 16845.38,-22991.04 16844.29,-22991.84 16843.22,-22992.67 16842.17,-22993.52 16841.13,-22994.39 16840.12,-22995.29 16839.13,-22996.22 16838.16,-22997.16 16837.22,-22998.13 16836.29,-22999.12 16835.39,-23000.13 16834.52,-23001.17 16833.67,-23002.22 16832.84,-23003.29 16832.04,-23004.38 16831.27,-23005.49 16830.52,-23006.62 16829.79,-23007.77 16829.1,-23008.93 16828.43,-23010.11 16827.79,-23011.3 16827.18,-23012.51 16826.6,-23013.73 16826.04,-23014.96 16825.52,-23016.21 16825.02,-23017.47 16824.55,-23018.74 16824.12,-23020.03 16823.71,-23021.32 16823.34,-23022.62 16822.99,-23023.93 16822.68,-23025.24 16822.39,-23026.57 16822.14,-23027.9 16821.92,-23029.23 16821.73,-23030.57 16821.57,-23031.92 16821.44,-23033.27 16821.35,-23034.62 16821.28,-23035.97 16821.25,-23037.32 16821.25,-23038 16736.75,-23038 16736.76,-23036.33 16736.83,-23033 16736.99,-23029.67 16737.23,-23026.34 16737.54,-23023.02 16737.93,-23019.71 16738.4,-23016.41 16738.94,-23013.12 16739.56,-23009.84 16740.26,-23006.58 16741.04,-23003.33 16741.89,-23000.11 16742.81,-22996.91 16743.81,-22993.72 16744.89,-22990.57 16746.04,-22987.44 16747.26,-22984.33 16748.55,-22981.26 16749.92,-22978.22 16751.36,-22975.21 16752.86,-22972.23 16754.44,-22969.29 16756.08,-22966.39 16757.8,-22963.53 16759.58,-22960.71 16761.42,-22957.93 16763.33,-22955.2 16765.3,-22952.51 16767.34,-22949.87 16769.43,-22947.27 16771.59,-22944.73 16773.81,-22942.24 16776.08,-22939.8 16778.41,-22937.41 16780.8,-22935.08 16783.24,-22932.81 16785.73,-22930.59 16788.27,-22928.43 16790.87,-22926.34 16793.51,-22924.3 16796.2,-22922.33 16798.93,-22920.42 16801.71,-22918.58 16804.53,-22916.8 16807.39,-22915.08 16810.29,-22913.44 16813.23,-22911.86 16816.21,-22910.36 16819.22,-22908.92 16822.26,-22907.55 16825.33,-22906.26 16828.44,-22905.04 16831.57,-22903.89 16834.72,-22902.81 16837.91,-22901.81 16841.11,-22900.89 16844.33,-22900.04 16847.58,-22899.26 16850.84,-22898.56 16854.12,-22897.94 16857.41,-22897.4 16860.71,-22896.93 16864.02,-22896.54 16867.34,-22896.23 16870.67,-22895.99 16874,-22895.83 16877.33,-22895.76"/>
<polygon id="000002B6361D9660" class="l101d251" points="16879,-22895.75 16879,-22980.25 16878.32,-22980.25 16876.97,-22980.28 16875.62,-22980.35 16874.27,-22980.44 16872.92,-22980.57 16871.57,-22980.73 16870.23,-22980.92 16868.9,-22981.14 16867.57,-22981.39 16866.24,-22981.68 16864.93,-22981.99 16863.62,-22982.34 16862.32,-22982.71 16861.03,-22983.12 16859.74,-22983.55 16858.47,-22984.02 16857.21,-22984.52 16855.96,-22985.04 16854.73,-22985.6 16853.51,-22986.18 16852.3,-22986.79 16851.11,-22987.43 16849.93,-22988.1 16848.77,-22988.79 16847.62,-22989.52 16846.49,-22990.27 16845.38,-22991.04 16844.29,-22991.84 16843.22,-22992.67 16842.17,-22993.52 16841.13,-22994.39 16840.12,-22995.29 16839.13,-22996.22 16838.16,-22997.16 16837.22,-22998.13 16836.29,-22999.12 16835.39,-23000.13 16834.52,-23001.17 16833.67,-23002.22 16832.84,-23003.29 16832.04,-23004.38 16831.27,-23005.49 16830.52,-23006.62 16829.79,-23007.77 16829.1,-23008.93 16828.43,-23010.11 16827.79,-23011.3 16827.18,-23012.51 16826.6,-23013.73 16826.04,-23014.96 16825.52,-23016.21 16825.02,-23017.47 16824.55,-23018.74 16824.12,-23020.03 16823.71,-23021.32 16823.34,-23022.62 16822.99,-23023.93 16822.68,-23025.24 16822.39,-23026.57 16822.14,-23027.9 16821.92,-23029.23 16821.73,-23030.57 16821.57,-23031.92 16821.44,-23033.27 16821.35,-23034.62 16821.28,-23035.97 16821.25,-23037.32 16821.25,-23038 16736.75,-23038 16736.76,-23036.33 16736.83,-23033 16736.99,-23029.67 16737.23,-23026.34 16737.54,-23023.02 16737.93,-23019.71 16738.4,-23016.41 16738.94,-23013.12 16739.56,-23009.84 16740.26,-23006.58 16741.04,-23003.33 16741.89,-23000.11 16742.81,-22996.91 16743.81,-22993.72 16744.89,-22990.57 16746.04,-22987.44 16747.26,-22984.33 16748.55,-22981.26 16749.92,-22978.22 16751.36,-22975.21 16752.86,-22972.23 16754.44,-22969.29 16756.08,-22966.39 16757.8,-22963.53 16759.58,-22960.71 16761.42,-22957.93 16763.33,-22955.2 16765.3,-22952.51 16767.34,-22949.87 16769.43,-22947.27 16771.59,-22944.73 16773.81,-22942.24 16776.08,-22939.8 16778.41,-22937.41 16780.8,-22935.08 16783.24,-22932.81 16785.73,-22930.59 16788.27,-22928.43 16790.87,-22926.34 16793.51,-22924.3 16796.2,-22922.33 16798.93,-22920.42 16801.71,-22918.58 16804.53,-22916.8 16807.39,-22915.08 16810.29,-22913.44 16813.23,-22911.86 16816.21,-22910.36 16819.22,-22908.92 16822.26,-22907.55 16825.33,-22906.26 16828.44,-22905.04 16831.57,-22903.89 16834.72,-22902.81 16837.91,-22901.81 16841.11,-22900.89 16844.33,-22900.04 16847.58,-22899.26 16850.84,-22898.56 16854.12,-22897.94 16857.41,-22897.4 16860.71,-22896.93 16864.02,-22896.54 16867.34,-22896.23 16870.67,-22895.99 16874,-22895.83 16877.33,-22895.76"/>
<polygon id="000002B6361D90B0" class="l101d251" points="16879,-22895.75 16879,-22980.25 16878.32,-22980.25 16876.97,-22980.28 16875.62,-22980.35 16874.27,-22980.44 16872.92,-22980.57 16871.57,-22980.73 16870.23,-22980.92 16868.9,-22981.14 16867.57,-22981.39 16866.24,-22981.68 16864.93,-22981.99 16863.62,-22982.34 16862.32,-22982.71 16861.03,-22983.12 16859.74,-22983.55 16858.47,-22984.02 16857.21,-22984.52 16855.96,-22985.04 16854.73,-22985.6 16853.51,-22986.18 16852.3,-22986.79 16851.11,-22987.43 16849.93,-22988.1 16848.77,-22988.79 16847.62,-22989.52 16846.49,-22990.27 16845.38,-22991.04 16844.29,-22991.84 16843.22,-22992.67 16842.17,-22993.52 16841.13,-22994.39 16840.12,-22995.29 16839.13,-22996.22 16838.16,-22997.16 16837.22,-22998.13 16836.29,-22999.12 16835.39,-23000.13 16834.52,-23001.17 16833.67,-23002.22 16832.84,-23003.29 16832.04,-23004.38 16831.27,-23005.49 16830.52,-23006.62 16829.79,-23007.77 16829.1,-23008.93 16828.43,-23010.11 16827.79,-23011.3 16827.18,-23012.51 16826.6,-23013.73 16826.04,-23014.96 16825.52,-23016.21 16825.02,-23017.47 16824.55,-23018.74 16824.12,-23020.03 16823.71,-23021.32 16823.34,-23022.62 16822.99,-23023.93 16822.68,-23025.24 16822.39,-23026.57 16822.14,-23027.9 16821.92,-23029.23 16821.73,-23030.57 16821.57,-23031.92 16821.44,-23033.27 16821.35,-23034.62 16821.28,-23035.97 16821.25,-23037.32 16821.25,-23038 16736.75,-23038 16736.76,-23036.33 16736.83,-23033 16736.99,-23029.67 16737.23,-23026.34 16737.54,-23023.02 16737.93,-23019.71 16738.4,-23016.41 16738.94,-23013.12 16739.56,-23009.84 16740.26,-23006.58 16741.04,-23003.33 16741.89,-23000.11 16742.81,-22996.91 16743.81,-22993.72 16744.89,-22990.57 16746.04,-22987.44 16747.26,-22984.33 16748.55,-22981.26 16749.92,-22978.22 16751.36,-22975.21 16752.86,-22972.23 16754.44,-22969.29 16756.08,-22966.39 16757.8,-22963.53 16759.58,-22960.71 16761.42,-22957.93 16763.33,-22955.2 16765.3,-22952.51 16767.34,-22949.87 16769.43,-22947.27 16771.59,-22944.73 16773.81,-22942.24 16776.08,-22939.8 16778.41,-22937.41 16780.8,-22935.08 16783.24,-22932.81 16785.73,-22930.59 16788.27,-22928.43 16790.87,-22926.34 16793.51,-22924.3 16796.2,-22922.33 16798.93,-22920.42 16801.71,-22918.58 16804.53,-22916.8 16807.39,-22915.08 16810.29,-22913.44 16813.23,-22911.86 16816.21,-22910.36 16819.22,-22908.92 16822.26,-22907.55 16825.33,-22906.26 16828.44,-22905.04 16831.57,-22903.89 16834.72,-22902.81 16837.91,-22901.81 16841.11,-22900.89 16844.33,-22900.04 16847.58,-22899.26 16850.84,-22898.56 16854.12,-22897.94 16857.41,-22897.4 16860.71,-22896.93 16864.02,-22896.54 16867.34,-22896.23 16870.67,-22895.99 16874,-22895.83 16877.33,-22895.76"/>
<polygon id="000002B6361D9890" class="l101d251" points="16879,-22895.75 16879,-22980.25 16878.32,-22980.25 16876.97,-22980.28 16875.62,-22980.35 16874.27,-22980.44 16872.92,-22980.57 16871.57,-22980.73 16870.23,-22980.92 16868.9,-22981.14 16867.57,-22981.39 16866.24,-22981.68 16864.93,-22981.99 16863.62,-22982.34 16862.32,-22982.71 16861.03,-22983.12 16859.74,-22983.55 16858.47,-22984.02 16857.21,-22984.52 16855.96,-22985.04 16854.73,-22985.6 16853.51,-22986.18 16852.3,-22986.79 16851.11,-22987.43 16849.93,-22988.1 16848.77,-22988.79 16847.62,-22989.52 16846.49,-22990.27 16845.38,-22991.04 16844.29,-22991.84 16843.22,-22992.67 16842.17,-22993.52 16841.13,-22994.39 16840.12,-22995.29 16839.13,-22996.22 16838.16,-22997.16 16837.22,-22998.13 16836.29,-22999.12 16835.39,-23000.13 16834.52,-23001.17 16833.67,-23002.22 16832.84,-23003.29 16832.04,-23004.38 16831.27,-23005.49 16830.52,-23006.62 16829.79,-23007.77 16829.1,-23008.93 16828.43,-23010.11 16827.79,-23011.3 16827.18,-23012.51 16826.6,-23013.73 16826.04,-23014.96 16825.52,-23016.21 16825.02,-23017.47 16824.55,-23018.74 16824.12,-23020.03 16823.71,-23021.32 16823.34,-23022.62 16822.99,-23023.93 16822.68,-23025.24 16822.39,-23026.57 16822.14,-23027.9 16821.92,-23029.23 16821.73,-23030.57 16821.57,-23031.92 16821.44,-23033.27 16821.35,-23034.62 16821.28,-23035.97 16821.25,-23037.32 16821.25,-23038 16736.75,-23038 16736.76,-23036.33 16736.83,-23033 16736.99,-23029.67 16737.23,-23026.34 16737.54,-23023.02 16737.93,-23019.71 16738.4,-23016.41 16738.94,-23013.12 16739.56,-23009.84 16740.26,-23006.58 16741.04,-23003.33 16741.89,-23000.11 16742.81,-22996.91 16743.81,-22993.72 16744.89,-22990.57 16746.04,-22987.44 16747.26,-22984.33 16748.55,-22981.26 16749.92,-22978.22 16751.36,-22975.21 16752.86,-22972.23 16754.44,-22969.29 16756.08,-22966.39 16757.8,-22963.53 16759.58,-22960.71 16761.42,-22957.93 16763.33,-22955.2 16765.3,-22952.51 16767.34,-22949.87 16769.43,-22947.27 16771.59,-22944.73 16773.81,-22942.24 16776.08,-22939.8 16778.41,-22937.41 16780.8,-22935.08 16783.24,-22932.81 16785.73,-22930.59 16788.27,-22928.43 16790.87,-22926.34 16793.51,-22924.3 16796.2,-22922.33 16798.93,-22920.42 16801.71,-22918.58 16804.53,-22916.8 16807.39,-22915.08 16810.29,-22913.44 16813.23,-22911.86 16816.21,-22910.36 16819.22,-22908.92 16822.26,-22907.55 16825.33,-22906.26 16828.44,-22905.04 16831.57,-22903.89 16834.72,-22902.81 16837.91,-22901.81 16841.11,-22900.89 16844.33,-22900.04 16847.58,-22899.26 16850.84,-22898.56 16854.12,-22897.94 16857.41,-22897.4 16860.71,-22896.93 16864.02,-22896.54 16867.34,-22896.23 16870.67,-22895.99 16874,-22895.83 16877.33,-22895.76"/>
<polygon id="000002B6361D9270" class="l101d251" points="16879,-22895.75 16879,-22980.25 16878.32,-22980.25 16876.97,-22980.28 16875.62,-22980.35 16874.27,-22980.44 16872.92,-22980.57 16871.57,-22980.73 16870.23,-22980.92 16868.9,-22981.14 16867.57,-22981.39 16866.24,-22981.68 16864.93,-22981.99 16863.62,-22982.34 16862.32,-22982.71 16861.03,-22983.12 16859.74,-22983.55 16858.47,-22984.02 16857.21,-22984.52 16855.96,-22985.04 16854.73,-22985.6 16853.51,-22986.18 16852.3,-22986.79 16851.11,-22987.43 16849.93,-22988.1 16848.77,-22988.79 16847.62,-22989.52 16846.49,-22990.27 16845.38,-22991.04 16844.29,-22991.84 16843.22,-22992.67 16842.17,-22993.52 16841.13,-22994.39 16840.12,-22995.29 16839.13,-22996.22 16838.16,-22997.16 16837.22,-22998.13 16836.29,-22999.12 16835.39,-23000.13 16834.52,-23001.17 16833.67,-23002.22 16832.84,-23003.29 16832.04,-23004.38 16831.27,-23005.49 16830.52,-23006.62 16829.79,-23007.77 16829.1,-23008.93 16828.43,-23010.11 16827.79,-23011.3 16827.18,-23012.51 16826.6,-23013.73 16826.04,-23014.96 16825.52,-23016.21 16825.02,-23017.47 16824.55,-23018.74 16824.12,-23020.03 16823.71,-23021.32 16823.34,-23022.62 16822.99,-23023.93 16822.68,-23025.24 16822.39,-23026.57 16822.14,-23027.9 16821.92,-23029.23 16821.73,-23030.57 16821.57,-23031.92 16821.44,-23033.27 16821.35,-23034.62 16821.28,-23035.97 16821.25,-23037.32 16821.25,-23038 16736.75,-23038 16736.76,-23036.33 16736.83,-23033 16736.99,-23029.67 16737.23,-23026.34 16737.54,-23023.02 16737.93,-23019.71 16738.4,-23016.41 16738.94,-23013.12 16739.56,-23009.84 16740.26,-23006.58 16741.04,-23003.33 16741.89,-23000.11 16742.81,-22996.91 16743.81,-22993.72 16744.89,-22990.57 16746.04,-22987.44 16747.26,-22984.33 16748.55,-22981.26 16749.92,-22978.22 16751.36,-22975.21 16752.86,-22972.23 16754.44,-22969.29 16756.08,-22966.39 16757.8,-22963.53 16759.58,-22960.71 16761.42,-22957.93 16763.33,-22955.2 16765.3,-22952.51 16767.34,-22949.87 16769.43,-22947.27 16771.59,-22944.73 16773.81,-22942.24 16776.08,-22939.8 16778.41,-22937.41 16780.8,-22935.08 16783.24,-22932.81 16785.73,-22930.59 16788.27,-22928.43 16790.87,-22926.34 16793.51,-22924.3 16796.2,-22922.33 16798.93,-22920.42 16801.71,-22918.58 16804.53,-22916.8 16807.39,-22915.08 16810.29,-22913.44 16813.23,-22911.86 16816.21,-22910.36 16819.22,-22908.92 16822.26,-22907.55 16825.33,-22906.26 16828.44,-22905.04 16831.57,-22903.89 16834.72,-22902.81 16837.91,-22901.81 16841.11,-22900.89 16844.33,-22900.04 16847.58,-22899.26 16850.84,-22898.56 16854.12,-22897.94 16857.41,-22897.4 16860.71,-22896.93 16864.02,-22896.54 16867.34,-22896.23 16870.67,-22895.99 16874,-22895.83 16877.33,-22895.76"/>
<use transform="translate(15115 -25315)" xlink:href="#1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2"/>
<use transform="translate(17164 -22938)" xlink:href="#1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2_405"/>
=======
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18291.35" height="7473" viewBox="12581.075 16793.425 18291.35 7473">
<defs>
<style type="text/css">
@@ -153,6 +67,5 @@
<use transform="translate(13416 -21038)" xlink:href="#MZI_SiN400_Si220_PIN_mod_1310_L1300_QY_202603"/>
<use transform="translate(26950 -22750) rotate(90)" xlink:href="#HT_150R_SiPPP_L500_100OHM_DUMMY_QY_202604"/>
<use transform="translate(28490 -19886)" xlink:href="#1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2"/>
>>>>>>> jingwen_main
</g>
</svg>

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@@ -13,16 +13,6 @@ type: project
version: "1.0.0"
# 1. External Ports (How this cell connects to the outside world)
<<<<<<< HEAD
ports: []
# 2. Instances (The sub-components dropped onto this canvas)
instances:
MMI_1:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
x: 1511.5
y: -2531.5
=======
pins:
- name: port_1_io1
layer: WG_CORE
@@ -96,7 +86,6 @@ instances:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
x: 2849.0
y: -1988.6
>>>>>>> jingwen_main
rotation: 0.0
flip: 0
flop: 0
@@ -104,17 +93,10 @@ instances:
settings:
length:
<<<<<<< HEAD
MMI_2:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/multimode_interferometers/1x2MMI_1310nm_TE_Silterra_202603_ZKY_v2
x: 1716.4
y: -2293.8
=======
DC_2:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/directional_couplers/DC_SiN400_99_1_1310_jyh_quantex_202603
x: 2656.9
y: -1992.8
>>>>>>> jingwen_main
rotation: 0.0
flip: 0
flop: 0
@@ -122,9 +104,6 @@ instances:
settings:
length:
<<<<<<< HEAD
elements: {}
=======
PD_1:
component: Silterra/EMO1_2ML_CU_Al_RDL/primitives/photodetectors/PD_1310_Monitor_Si220_Ge500_NPN_XHN_202604
x: 3151.7
@@ -150,20 +129,9 @@ elements:
pins:
- name: port_1_io1
role: io1
>>>>>>> jingwen_main
# 3. Bundles (Grouped links for multi-bus/parallel routing)
bundles:
output_bus:
routing_type: euler_bend
links:
<<<<<<< HEAD
- from: MMI_2:a1
to: MMI_1:b2
xsection: strip
family: optical
width: 0.45
radius: 10
routing_type: euler_bend
=======
>>>>>>> jingwen_main
Binary file not shown.
+17
View File
@@ -1237,6 +1237,22 @@ ${linksYaml}`;
return null;
};
const getRotatableNodeHandleDirection = (node, handleId) => {
if (!node || !handleId) return null;
if (node.type !== 'rotatableNode' && !(!node.data?.elementType && node.data?.componentName)) return null;
const ports = node.data && node.data.ports;
if (!ports || !ports[handleId]) return null;
const boxSize = normalizeBoxSize({ box_size: node.data && node.data.boxSize }, DEFAULT_COMPONENT_BOX_SIZE);
const handles = buildPortHandles(ports, {
rotation: Number((node.data && node.data.rotation) || 0),
flip: Boolean(node.data && node.data.flip),
flop: Boolean(node.data && node.data.flop),
boxSize
});
const found = handles.find(handle => handle.name === handleId);
return found ? found.position : null;
};
// Backward-compatible alias for same-type route crossing validation.
const findSameFamilyRouteCrossing = findSameTypeRouteCrossing;
@@ -1276,6 +1292,7 @@ ${linksYaml}`;
createComponentSymbolMetrics,
transformPortInfo,
getNodePortCanvasPoint,
getRotatableNodeHandleDirection,
buildPortHandles,
buildElementPorts,
buildElementPinEntries,
+30 -11
View File
@@ -1566,6 +1566,7 @@ Organization : OptiHK Limited
calculateLayoutBounds,
calculateCompositeBoxSize,
buildPortHandles,
getRotatableNodeHandleDirection,
buildElementPorts,
getElementPinName,
buildElementBoxSize,
@@ -1698,8 +1699,10 @@ Organization : OptiHK Limited
useEffect(() => {
const transformKey = `${data.rotation || 0}:${data.flip ? 1 : 0}:${data.flop ? 1 : 0}`;
if (prevTransformRef.current !== transformKey) {
updateNodeInternalsRef.current(id);
prevTransformRef.current = transformKey;
requestAnimationFrame(() => {
updateNodeInternalsRef.current(id);
});
}
}, [data.rotation, data.flip, data.flop, id]);
@@ -1719,6 +1722,16 @@ Organization : OptiHK Limited
top: Position.Top,
bottom: Position.Bottom
};
const rotateHandleDirection = (dir, rot) => {
const norm = ((rot % 360) + 360) % 360;
const map = {
0: { right: 'right', left: 'left', top: 'top', bottom: 'bottom' },
90: { right: 'bottom', left: 'top', top: 'left', bottom: 'right' },
180: { right: 'left', left: 'right', top: 'bottom', bottom: 'top' },
270: { right: 'top', left: 'bottom', top: 'right', bottom: 'left' }
};
return (map[norm] || map[0])[dir] || dir;
};
const componentSize = normalizeBoxSize({ box_size: data.boxSize }, DEFAULT_COMPONENT_BOX_SIZE);
const flippedPorts = useMemo(
() => {
@@ -1844,24 +1857,28 @@ Organization : OptiHK Limited
transformOrigin: 'center center',
pointerEvents: 'none'
}}>
{portHandles.map((portHandle) => (
{portHandles.map((portHandle) => {
const originalDir = portDirectionMap.get(portHandle.name) || portHandle.position;
const effectiveDir = rotateHandleDirection(originalDir, data.rotation || 0);
return (
<React.Fragment key={portHandle.name}>
<Handle
type="source"
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]}
position={handlePositionMap[effectiveDir]}
id={portHandle.name}
title={portHandle.name}
style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 10, pointerEvents: 'all' }}
/>
<Handle
type="target"
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]}
position={handlePositionMap[effectiveDir]}
id={portHandle.name}
title={portHandle.name}
style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 5, pointerEvents: 'all' }}
/>
</React.Fragment>
))}
);
})}
{portHandles.map((portHandle) => (
<span key={`label-${portHandle.name}`} className="port-name-label" style={portLabelStyle(portHandle)} title={portHandle.name}>
{portHandle.name}
@@ -4007,8 +4024,10 @@ Organization : OptiHK Limited
const targetEndpoint = `${edge.target}:${edge.targetHandle || ''}`;
const key = [sourceEndpoint, targetEndpoint].sort().join('<>');
const group = groups.get(key) || [];
const sourceDirection = getAnchorHandleRouteDirection(nodeMap[edge.source], edge.sourceHandle);
const targetDirection = getAnchorHandleRouteDirection(nodeMap[edge.target], edge.targetHandle);
const sourceDirection = getAnchorHandleRouteDirection(nodeMap[edge.source], edge.sourceHandle)
|| getRotatableNodeHandleDirection(nodeMap[edge.source], edge.sourceHandle);
const targetDirection = getAnchorHandleRouteDirection(nodeMap[edge.target], edge.targetHandle)
|| getRotatableNodeHandleDirection(nodeMap[edge.target], edge.targetHandle);
const usesAnchorDirection = Boolean(sourceDirection || targetDirection);
const hasRoutePoints = edge.data && Array.isArray(edge.data.points) && edge.data.points.length >= 2;
const directionalEdge = usesAnchorDirection
@@ -4031,7 +4050,7 @@ Organization : OptiHK Limited
};
});
return [...separatedEdges, ...rulerEdges];
}, [currentEdges, currentNodes, getAnchorHandleRouteDirection, rulerEdges]);
}, [currentEdges, currentNodes, getAnchorHandleRouteDirection, getRotatableNodeHandleDirection, rulerEdges]);
const [projectCompositeMap, setProjectCompositeMap] = useState({});
const [standaloneComposites, setStandaloneComposites] = useState([]);
@@ -6018,6 +6037,7 @@ Organization : OptiHK Limited
const route = currentLinkRoute;
const view = routeStyleForSettings(route, false);
const edgeId = `edge-${connection.source}-${connection.sourceHandle}-${connection.target}-${connection.targetHandle}-${Date.now()}`;
const nodeMap = Object.fromEntries(activePage.nodes.map(node => [node.id, node]));
const candidate = {
id: edgeId,
source: connection.source,
@@ -6027,9 +6047,8 @@ Organization : OptiHK Limited
type: view.type,
selectable: true,
style: view.style,
data: { route }
data: { route },
};
const nodeMap = Object.fromEntries(activePage.nodes.map(node => [node.id, node]));
const conflict = findSameTypeRouteCrossing(candidate, activePage.edges, nodeMap, technologyManifest);
if (conflict) {
const source = nodeMap[conflict.conflictEdge.source]?.data?.componentDisplayName || conflict.conflictEdge.source;
@@ -6043,7 +6062,7 @@ Organization : OptiHK Limited
: p
)));
addLog(`Connected ${connection.sourceHandle} to ${connection.targetHandle}.`);
}, [activePageId, activePage, rulerMode, currentLinkRoute, technologyManifest, addLog]);
}, [activePageId, activePage, rulerMode, currentLinkRoute, technologyManifest, addLog, getAnchorHandleRouteDirection]);
// Select custom route edges from their SVG hit target.
const handleRouteEdgeMouseDown = useCallback((event) => {
+829
View File
@@ -0,0 +1,829 @@
<!DOCTYPE html>
<html lang="en">
{% raw %}
<head>
<meta charset="UTF-8">
<title>mxPIC Core - Canvas</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/reactflow@11/dist/umd/index.js" crossorigin></script>
<link rel="stylesheet" href="https://unpkg.com/reactflow@11/dist/style.css" />
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
/* optihk Shared Dark Theme Variables */
:root {
--bg-main: #0f172a;
--bg-card: #1e293b;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--accent: #38bdf8;
--accent-hover: #0284c7;
--border: #334155;
--input-bg: #0b1120;
}
body,
html,
#root {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: 'Inter', sans-serif;
background-color: var(--bg-main);
color: var(--text-main);
overflow: hidden;
}
/* Custom Dark Scrollbars */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-main);
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
/* Tree View Styling */
details {
margin-left: 8px;
}
summary {
cursor: pointer;
padding: 4px 0;
color: var(--text-main);
}
.tree-folder summary {
font-weight: 500;
color: var(--accent);
}
.component-leaf {
cursor: grab;
padding: 4px 6px;
margin-left: 15px;
margin-top: 2px;
word-break: break-all;
white-space: normal;
border-radius: 4px;
color: var(--text-muted);
transition: background 0.2s ease, color 0.2s ease;
}
.component-leaf:hover {
background: var(--border);
color: var(--text-main);
}
/* Side Panel Blocks */
.left-block, .right-block {
background: var(--bg-main);
border: 1px solid var(--border);
border-radius: 6px;
margin-bottom: 12px;
overflow: hidden;
}
.left-block-header, .right-block-header {
background: var(--bg-card);
padding: 8px 12px;
font-weight: 600;
font-size: 0.85em;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 1px;
}
.left-block-body, .right-block-body {
padding: 12px;
font-size: 0.85em;
}
.placeholder-block {
border: 1px dashed var(--border);
padding: 12px;
color: var(--text-muted);
text-align: center;
background: var(--bg-main);
}
.toggle-btn {
background: none;
border: none;
font-size: 1.2em;
color: var(--text-muted);
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
transition: background 0.2s ease, color 0.2s ease;
}
.toggle-btn:hover {
background: var(--border);
color: var(--text-main);
}
/* Standard Form Inputs inside panels */
input[type="number"], input[type="text"] {
background-color: var(--input-bg);
border: 1px solid var(--border);
color: var(--text-main);
font-family: inherit;
font-size: 0.9em;
padding: 6px 10px;
border-radius: 4px;
outline: none;
width: 100%;
box-sizing: border-box;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
input[type="number"]:focus, input[type="text"]:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.2);
}
label {
font-weight: 500;
color: var(--text-muted);
margin-bottom: 4px;
display: block;
}
/* ReactFlow Dark Mode Overrides */
.react-flow__controls button {
background-color: var(--bg-card) !important;
border-bottom: 1px solid var(--border) !important;
fill: var(--text-main) !important;
}
.react-flow__controls button:hover {
background-color: var(--border) !important;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useRef, useCallback, useMemo, memo } = React;
const {
ReactFlow,
ReactFlowProvider,
useNodesState,
useEdgesState,
Controls,
Background,
useReactFlow,
addEdge,
Handle,
Position,
useUpdateNodeInternals,
} = window.ReactFlow;
// --- NODE DESIGN (Dark CAD Style) ---
const RotatableNode = ({ id, data, selected }) => {
const updateNodeInternals = useUpdateNodeInternals();
useEffect(() => {
updateNodeInternals(id);
}, [data.rotation, updateNodeInternals, id]);
const baseHandleStyle = {
width: 10, height: 10,
background: 'var(--bg-main)',
border: '2px solid var(--accent)',
borderRadius: '50%',
};
const leftTopPort = { ...baseHandleStyle, top: '24%', transform: 'translate(-50%, -50%)' };
const leftBottomPort = { ...baseHandleStyle, top: '76%', transform: 'translate(-50%, -50%)' };
const rightTopPort = { ...baseHandleStyle, top: '24%', transform: 'translate(50%, -50%)' };
const rightBottomPort = { ...baseHandleStyle, top: '76%', transform: 'translate(50%, -50%)' };
return (
<div style={{
padding: '10px 20px',
border: selected ? '2px solid var(--accent)' : '1px solid var(--border)',
borderRadius: 6,
background: 'var(--bg-card)',
color: 'var(--text-main)',
minWidth: 100, textAlign: 'center',
position: 'relative', transform: `rotate(${data.rotation || 0}deg)`,
transition: selected ? 'none' : 'transform 0.1s ease',
boxSizing: 'border-box',
boxShadow: selected ? '0 0 15px rgba(56, 189, 248, 0.2)' : '0 4px 6px rgba(0,0,0,0.3)',
fontFamily: "'Inter', sans-serif",
fontSize: '0.85rem'
}}>
<div>{data.componentDisplayName}</div>
<Handle type="source" position={Position.Left} id="port-lt-source" style={{ ...leftTopPort, zIndex: 10 }} />
<Handle type="target" position={Position.Left} id="port-lt-target" style={{ ...leftTopPort, zIndex: 5 }} />
<Handle type="source" position={Position.Left} id="port-lb-source" style={{ ...leftBottomPort, zIndex: 10 }} />
<Handle type="target" position={Position.Left} id="port-lb-target" style={{ ...leftBottomPort, zIndex: 5 }} />
<Handle type="source" position={Position.Right} id="port-rt-source" style={{ ...rightTopPort, zIndex: 10 }} />
<Handle type="target" position={Position.Right} id="port-rt-target" style={{ ...rightTopPort, zIndex: 5 }} />
<Handle type="source" position={Position.Right} id="port-rb-source" style={{ ...rightBottomPort, zIndex: 10 }} />
<Handle type="target" position={Position.Right} id="port-rb-target" style={{ ...rightBottomPort, zIndex: 5 }} />
</div>
);
};
const TreeNode = ({ name, children }) => {
if (children && children.__type__ === 'component') {
const componentName = children.__name__;
const handleDragStart = (event) => {
event.dataTransfer.setData('application/reactflow', componentName);
event.dataTransfer.effectAllowed = 'move';
};
return (
<div className="component-leaf" draggable onDragStart={handleDragStart}>
<span style={{color: 'var(--accent)', marginRight: '4px'}}></span> {name}
</div>
);
}
const hasChildren = children && Object.keys(children).length > 0;
return (
<details>
<summary className="tree-folder">
<span style={{ wordBreak: 'break-all', whiteSpace: 'normal' }}>📂 {name}</span>
</summary>
{hasChildren &&
Object.entries(children).map(([childName, childData]) => (
<TreeNode key={childName} name={childName} children={childData} />
))
}
</details>
);
};
const LeftPanel = ({ library, treeKey, expanded, onToggle, treeRef, width }) => (
<aside style={{
width: width, background: 'var(--bg-card)', borderRight: '1px solid var(--border)',
padding: 12, display: 'flex', flexDirection: 'column', height: '100%',
boxSizing: 'border-box', overflowY: 'auto'
}}>
<div className="left-block">
<div className="left-block-header">
<span>PDK Libraries</span>
<button className="toggle-btn" onClick={onToggle} title={expanded ? 'Collapse all' : 'Expand all'}>
{expanded ? '▾' : '▸'}
</button>
</div>
<div className="left-block-body" style={{ maxHeight: '45vh', overflowY: 'auto' }} key={treeKey} ref={treeRef}>
{library && Object.keys(library).length > 0 ? (
Object.entries(library).map(([key, value]) => (
<TreeNode key={key} name={key} children={value} />
))
) : (
<p style={{ color: 'var(--text-muted)', fontStyle: 'italic' }}>Loading library...</p>
)}
</div>
</div>
<div className="left-block">
<div className="left-block-header">Routing modes</div>
<div className="left-block-body">
<ul style={{ paddingLeft: 20, margin: 0, color: 'var(--text-muted)', lineHeight: '1.8' }}>
<li>Single mode wires</li>
<li>Multi-mode wires</li>
<li>DC electrical wires</li>
<li>RF electrical wires</li>
</ul>
</div>
</div>
<div className="left-block" style={{ marginTop: 'auto' }}>
<div className="left-block-header">Session</div>
<div className="left-block-body" style={{color: 'var(--text-muted)'}}>
<div style={{marginBottom: '4px'}}>Name: XXXXXX</div>
<div style={{marginBottom: '10px'}}>ID: 12345678</div>
<button disabled style={{
background: 'var(--border)', color: 'var(--text-muted)',
border: 'none', padding: '6px 12px', borderRadius: '4px', width: '100%'
}}>Log out</button>
</div>
</div>
</aside>
);
const RightPanel = memo(({ selectedNode, width, onRenameComponent }) => {
const [componentData, setComponentData] = useState(null);
const [loading, setLoading] = useState(false);
const [enlarged, setEnlarged] = useState(null);
const { setNodes } = useReactFlow();
const [editingComponentName, setEditingComponentName] = useState(false);
const [tempComponentName, setTempComponentName] = useState('');
const [localX, setLocalX] = useState('');
const [localY, setLocalY] = useState('');
const [localRotation, setLocalRotation] = useState('');
useEffect(() => {
const nodeId = selectedNode?.id;
if (!nodeId) {
setComponentData(null);
setLoading(false);
return;
}
const compName = selectedNode?.data?.componentName;
if (!compName) {
setComponentData(null);
setLoading(false);
return;
}
if (componentData && componentData.name === compName && componentData.nodeId === nodeId) return;
setLoading(true);
fetch(`/api/component/${encodeURIComponent(compName)}`)
.then(r => r.json())
.then(data => {
setComponentData({ ...data, nodeId: nodeId, componentDisplayName: selectedNode.data.componentDisplayName || data.name });
setLoading(false);
})
.catch(() => setLoading(false));
}, [selectedNode?.id, selectedNode?.data?.componentName, selectedNode?.data?.componentDisplayName]);
useEffect(() => {
if (selectedNode) {
setLocalX(selectedNode.position.x.toFixed(3));
setLocalY(selectedNode.position.y.toFixed(3));
setLocalRotation(((selectedNode.data?.rotation || 0)).toFixed(3));
}
}, [selectedNode?.position.x, selectedNode?.position.y, selectedNode?.data?.rotation, selectedNode?.id]);
const updatePosition = useCallback((id, axis, value) => {
const val = parseFloat(value);
if (isNaN(val)) return;
setNodes(nds => nds.map(n => n.id === id ? { ...n, position: { ...n.position, [axis]: val } } : n));
}, [setNodes]);
const updateRotation = useCallback((id, value) => {
const val = parseFloat(value);
if (isNaN(val)) return;
const clamped = Math.min(180, Math.max(-180, val));
setNodes(nds => nds.map(n => n.id === id ? { ...n, data: { ...n.data, rotation: clamped } } : n));
}, [setNodes]);
const formatPort = (port) => {
if (!port) return '—';
return `x:${port.x ?? '?'}, y:${port.y ?? '?'}, a:${port.a ?? '?'}, w:${port.width ?? '?'}`;
};
const currentRotation = selectedNode?.data?.rotation ?? 0;
const currentComponentDisplayName = selectedNode?.data?.componentDisplayName || '';
const handleStartEditName = () => {
setTempComponentName(currentComponentDisplayName);
setEditingComponentName(true);
};
const handleSaveName = () => {
const newName = tempComponentName.trim();
if (newName && selectedNode) {
onRenameComponent(selectedNode.id, newName);
}
setEditingComponentName(false);
};
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
handleSaveName();
} else if (e.key === 'Escape') {
setEditingComponentName(false);
}
};
return (
<aside style={{
width: width, background: 'var(--bg-card)', borderLeft: '1px solid var(--border)',
padding: 12, display: 'flex', flexDirection: 'column', height: '100%',
boxSizing: 'border-box', overflowY: 'auto'
}}>
<div className="right-block">
<div className="right-block-header">Transforms</div>
<div className="right-block-body">
{selectedNode ? (
<div>
<label>X Coordinate</label>
<input
type="number"
step="1"
value={localX}
onChange={(e) => setLocalX(e.target.value)}
onBlur={() => {
const val = parseFloat(localX);
if (!isNaN(val) && selectedNode) {
updatePosition(selectedNode.id, 'x', val);
setLocalX(val.toFixed(3));
} else if (selectedNode) {
setLocalX(selectedNode.position.x.toFixed(3));
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') e.currentTarget.blur();
}}
/>
<br /><br />
<label>Y Coordinate</label>
<input
type="number"
step="1"
value={localY}
onChange={(e) => setLocalY(e.target.value)}
onBlur={() => {
const val = parseFloat(localY);
if (!isNaN(val) && selectedNode) {
updatePosition(selectedNode.id, 'y', val);
setLocalY(val.toFixed(3));
} else if (selectedNode) {
setLocalY(selectedNode.position.y.toFixed(3));
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') e.currentTarget.blur();
}}
/>
<br /><br />
<label>Angle (deg)</label>
<input
type="number"
step="1"
value={localRotation}
onChange={(e) => setLocalRotation(e.target.value)}
onBlur={() => {
const val = parseFloat(localRotation);
if (!isNaN(val) && selectedNode) {
updateRotation(selectedNode.id, val);
setLocalRotation(val.toFixed(3));
} else if (selectedNode) {
setLocalRotation(((selectedNode.data?.rotation || 0)).toFixed(3));
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') e.currentTarget.blur();
}}
/>
</div>
) : (
<p style={{ color: 'var(--text-muted)', fontStyle: 'italic', textAlign: 'center' }}>Select a node to inspect</p>
)}
</div>
</div>
{selectedNode?.data?.componentName && (
<div className="right-block">
<div className="right-block-header">Parameters</div>
<div className="right-block-body">
{loading ? (
<p style={{color: 'var(--text-muted)'}}>Loading data...</p>
) : componentData ? (
<>
<div style={{ marginBottom: '15px' }}>
<label>Instance Name</label>
{editingComponentName ? (
<input
type="text"
value={tempComponentName}
onChange={(e) => setTempComponentName(e.target.value)}
onBlur={handleSaveName}
onKeyDown={handleKeyDown}
autoFocus
/>
) : (
<div
style={{
cursor: 'pointer',
padding: '6px 8px',
backgroundColor: 'var(--input-bg)',
border: '1px solid var(--border)',
borderRadius: '4px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
wordBreak: 'break-all',
color: 'var(--accent)'
}}
onClick={handleStartEditName}
title="Click to edit"
>
<span>{currentComponentDisplayName || componentData.name}</span>
<span style={{fontSize: '12px', color: 'var(--text-muted)'}}></span>
</div>
)}
</div>
<div style={{color: 'var(--text-muted)', lineHeight: '1.6'}}>
<p style={{ margin: '0 0 8px 0', wordBreak: 'break-all' }}>
<strong style={{color: 'var(--text-main)'}}>Cell:</strong> {componentData.name}
</p>
<p style={{ margin: '0 0 8px 0' }}>
<strong style={{color: 'var(--text-main)'}}>Foundry:</strong> {componentData.foundry}<br/>
<strong style={{color: 'var(--text-main)'}}>Process:</strong> {componentData.process}
</p>
</div>
<p style={{color: 'var(--text-main)', fontWeight: '500', marginBottom: '4px'}}>Ports:</p>
<ul style={{ paddingLeft: 15, margin: '0 0 15px 0', color: 'var(--text-muted)' }}>
{componentData.ports && Object.entries(componentData.ports).map(([portName, portInfo]) => (
<li key={portName} style={{ letterSpacing: '0.5px' }}>
<span style={{color: 'var(--accent)'}}>{portName}</span>: {formatPort(portInfo)}
</li>
))}
</ul>
<p style={{color: 'var(--text-main)', fontWeight: '500', marginBottom: '4px'}}>Preview:</p>
<div style={{
border: '1px solid var(--border)', width: '100%', height: 100,
display: 'flex', alignItems: 'center', justifyContent: 'center',
background: 'var(--input-bg)', borderRadius: '4px',
overflow: 'hidden',
}}>
<img
src={`/api/component/${encodeURIComponent(componentData.name)}/image`}
alt="Component layout"
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', cursor: 'pointer' }}
onClick={() => setEnlarged(`/api/component/${encodeURIComponent(componentData.name)}/image`)}
onError={(e) => {
e.currentTarget.style.display = 'none';
e.currentTarget.parentElement.innerHTML = '<span style="color:var(--text-muted)">No preview</span>';
}}
/>
</div>
</>
) : (
<p style={{ color: 'var(--text-muted)' }}>No data available</p>
)}
</div>
</div>
)}
<div className="right-block" style={{ marginTop: 'auto' }}>
<div className="right-block-header">Inverse Design</div>
<div className="right-block-body placeholder-block">Requires AI Upgrade</div>
</div>
{enlarged && (
<div
style={{
position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
backgroundColor: 'rgba(15, 23, 42, 0.9)', zIndex: 1000,
display: 'flex', alignItems: 'center', justifyContent: 'center',
cursor: 'zoom-out',
backdropFilter: 'blur(4px)'
}}
onClick={() => setEnlarged(null)}
>
<img
src={enlarged}
alt="Enlarged layout"
style={{ maxWidth: '90%', maxHeight: '90%', objectFit: 'contain', border: '1px solid var(--border)', background: 'var(--bg-main)' }}
/>
</div>
)}
</aside>
);
}, (prevProps, nextProps) => {
const prev = prevProps.selectedNode;
const next = nextProps.selectedNode;
if (prev?.id !== next?.id) return false;
if (prev?.position?.x !== next?.position?.x) return false;
if (prev?.position?.y !== next?.position?.y) return false;
if (prev?.data?.rotation !== next?.data?.rotation) return false;
if (prev?.data?.componentName !== next?.data?.componentName) return false;
if (prev?.data?.componentDisplayName !== next?.data?.componentDisplayName) return false;
if (prevProps.width !== nextProps.width) return false;
return true;
});
const ResizeHandle = ({ onMouseDown }) => (
<div
onMouseDown={onMouseDown}
style={{
width: 6, cursor: 'col-resize', background: 'transparent',
transition: 'background 0.2s', zIndex: 5, flexShrink: 0,
}}
onMouseEnter={(e) => e.currentTarget.style.background = 'var(--accent)'}
onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
/>
);
function App() {
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const reactFlowInstance = useReactFlow();
const [library, setLibrary] = useState(null);
const [treeKey, setTreeKey] = useState(0);
const [expanded, setExpanded] = useState(false);
const treeContainerRef = useRef(null);
const [leftWidth, setLeftWidth] = useState(260);
const [rightWidth, setRightWidth] = useState(260);
const [dragging, setDragging] = useState(null);
const [gridSnap, setGridSnap] = useState(false);
const componentCounterRef = useRef(1);
const generateComponentDisplayName = useCallback(() => {
const name = `component_${componentCounterRef.current}`;
componentCounterRef.current += 1;
return name;
}, []);
const renameComponent = useCallback((nodeId, newComponentDisplayName) => {
setNodes(nds => nds.map(n => {
if (n.id === nodeId) {
return {
...n,
data: {
...n.data,
componentDisplayName: newComponentDisplayName
}
};
}
return n;
}));
}, [setNodes]);
const fetchLibrary = useCallback(async () => {
try {
const res = await fetch('/api/library');
const data = await res.json();
setLibrary(data);
} catch (err) {
console.error('Failed to fetch library', err);
}
}, []);
useEffect(() => { fetchLibrary(); }, [fetchLibrary]);
const selectedNode = useMemo(() => nodes.find(n => n.selected), [nodes]);
const onDragOver = useCallback((event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);
const onDrop = useCallback((event) => {
event.preventDefault();
const type = event.dataTransfer.getData('application/reactflow');
if (!type) return;
const position = reactFlowInstance.screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
const componentDisplayName = generateComponentDisplayName();
const newNode = {
id: Date.now().toString(),
type: 'rotatableNode',
position,
data: {
label: type,
componentName: type,
rotation: 0,
componentDisplayName: componentDisplayName
},
};
setNodes((nds) => nds.concat(newNode));
}, [setNodes, reactFlowInstance, generateComponentDisplayName]);
const onConnect = useCallback((connection) => {
setEdges((eds) => addEdge({ ...connection, type: 'smoothstep', style: { stroke: 'var(--accent)', strokeWidth: 2 } }, eds));
}, [setEdges]);
const expandAll = useCallback(() => {
if (treeContainerRef.current) {
treeContainerRef.current.querySelectorAll('details').forEach(d => d.open = true);
}
}, []);
const collapseAll = useCallback(() => setTreeKey(k => k + 1), []);
const handleToggle = useCallback(() => {
if (expanded) { collapseAll(); setExpanded(false); }
else { expandAll(); setExpanded(true); }
}, [expanded, expandAll, collapseAll]);
const handleResizeStart = useCallback((side) => (e) => {
e.preventDefault();
setDragging(side);
}, []);
useEffect(() => {
if (!dragging) return;
const onMouseMove = (e) => {
if (dragging === 'left') {
setLeftWidth(Math.min(500, Math.max(150, e.clientX)));
} else if (dragging === 'right') {
const newWidth = window.innerWidth - e.clientX;
setRightWidth(Math.min(500, Math.max(150, newWidth)));
}
};
const onMouseUp = () => setDragging(null);
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
return () => {
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', onMouseUp);
};
}, [dragging]);
const toggleGridSnap = useCallback(() => {
setGridSnap(prev => !prev);
}, []);
return (
<div style={{ display: 'flex', width: '100%', height: '100%', userSelect: dragging ? 'none' : 'auto' }}>
<LeftPanel
library={library} treeKey={treeKey} expanded={expanded}
onToggle={handleToggle} treeRef={treeContainerRef} width={leftWidth}
/>
<ResizeHandle onMouseDown={handleResizeStart('left')} />
<div style={{ flex: 1, position: 'relative' }}>
{/* Grid Snap Toggle Switch */}
<div style={{
position: 'absolute', top: 15, right: 15, zIndex: 10,
display: 'flex', alignItems: 'center', gap: 10,
background: 'var(--bg-card)', padding: '6px 12px', borderRadius: '8px',
border: '1px solid var(--border)', boxShadow: '0 4px 6px rgba(0,0,0,0.3)'
}}>
<span style={{
fontSize: '0.85em', fontWeight: '500', color: 'var(--text-main)', userSelect: 'none'
}}>Snap to Grid</span>
<div
onClick={toggleGridSnap}
style={{
width: 40, height: 20, borderRadius: 10,
background: gridSnap ? 'var(--accent)' : 'var(--input-bg)',
border: '1px solid ' + (gridSnap ? 'var(--accent)' : 'var(--border)'),
cursor: 'pointer', display: 'flex', alignItems: 'center',
padding: '0 2px', transition: 'background 0.3s, border-color 0.3s',
}}
>
<div style={{
width: 16, height: 16, borderRadius: '50%',
background: '#fff',
transform: gridSnap ? 'translateX(20px)' : 'translateX(0)',
transition: 'transform 0.2s',
}} />
</div>
</div>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onDragOver={onDragOver}
onDrop={onDrop}
onConnect={onConnect}
nodeTypes={{ rotatableNode: RotatableNode }}
fitView
snapToGrid={gridSnap}
snapGrid={[10, 10]}
nodesDraggable={true}
nodesConnectable={true}
elementsSelectable={true}
connectionRadius={50}
>
<Controls style={{ bottom: 15, left: 15 }} />
{/* Dark mode background for the canvas */}
<Background color="#334155" gap={20} size={1} />
</ReactFlow>
</div>
<ResizeHandle onMouseDown={handleResizeStart('right')} />
<RightPanel
selectedNode={selectedNode}
width={rightWidth}
onRenameComponent={renameComponent}
/>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ReactFlowProvider>
<App />
</ReactFlowProvider>
);
</script>
</body>
</html>
{% endraw %}
+750
View File
@@ -0,0 +1,750 @@
<!DOCTYPE html>
<html lang="en">
{% raw %}
<head>
<meta charset="UTF-8">
<title>Canvas with PDK Library Component Name & Rotation</title>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/reactflow@11/dist/umd/index.js" crossorigin></script>
<link rel="stylesheet" href="https://unpkg.com/reactflow@11/dist/style.css" />
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
body,
html,
#root {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
details {
margin-left: 8px;
}
summary {
cursor: pointer;
}
.left-block {
border: 1px solid #ccc;
border-radius: 4px;
margin-bottom: 8px;
}
.left-block-header {
background: #e0e0e0;
padding: 6px 10px;
font-weight: bold;
font-size: 0.9em;
border-bottom: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: space-between;
}
.left-block-body {
padding: 8px 10px;
font-size: 0.85em;
}
.tree-folder summary {
font-weight: bold;
}
.right-block {
border: 1px solid #ccc;
border-radius: 4px;
margin-bottom: 8px;
}
.right-block-header {
background: #e0e0e0;
padding: 6px 10px;
font-weight: bold;
font-size: 0.9em;
border-bottom: 1px solid #ccc;
}
.right-block-body {
padding: 8px 10px;
font-size: 0.85em;
}
.placeholder-block {
border: 1px dashed #bbb;
padding: 8px;
color: #888;
font-size: 0.85em;
background: #f9f9f9;
}
.toggle-btn {
background: none;
border: none;
font-size: 1.8em;
cursor: pointer;
padding: 2px 4px;
line-height: 1;
border-radius: 3px;
}
.toggle-btn:hover {
background: #d0d0d0;
}
.component-leaf {
cursor: grab;
padding: 2px 4px;
margin-left: 20px;
word-break: break-all;
white-space: normal;
}
.component-leaf:hover {
background: #e6f7ff;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useRef, useCallback, useMemo, memo } = React;
const {
ReactFlow,
ReactFlowProvider,
useNodesState,
useEdgesState,
Controls,
Background,
useReactFlow,
addEdge,
Handle,
Position,
useUpdateNodeInternals,
} = window.ReactFlow;
const RotatableNode = ({ id, data, selected }) => {
const updateNodeInternals = useUpdateNodeInternals();
useEffect(() => {
updateNodeInternals(id);
}, [data.rotation, updateNodeInternals, id]);
const baseHandleStyle = {
width: 14, height: 14,
background: '#555',
border: 'none',
borderRadius: '50%',
};
const leftTopPort = { ...baseHandleStyle, top: '24%', transform: 'translate(-50%, -50%)' };
const leftBottomPort = { ...baseHandleStyle, top: '76%', transform: 'translate(-50%, -50%)' };
const rightTopPort = { ...baseHandleStyle, top: '24%', transform: 'translate(50%, -50%)' };
const rightBottomPort = { ...baseHandleStyle, top: '76%', transform: 'translate(50%, -50%)' };
return (
<div style={{
padding: '10px 20px', border: '1px solid #333', borderRadius: 6,
background: '#fff', minWidth: 100, textAlign: 'center',
position: 'relative', transform: `rotate(${data.rotation || 0}deg)`,
transition: selected ? 'none' : 'transform 0.1s ease',
boxSizing: 'border-box',
}}>
<div>{data.componentDisplayName}</div>
<Handle type="source" position={Position.Left} id="port-lt-source" style={{ ...leftTopPort, zIndex: 10 }} />
<Handle type="target" position={Position.Left} id="port-lt-target" style={{ ...leftTopPort, zIndex: 5 }} />
<Handle type="source" position={Position.Left} id="port-lb-source" style={{ ...leftBottomPort, zIndex: 10 }} />
<Handle type="target" position={Position.Left} id="port-lb-target" style={{ ...leftBottomPort, zIndex: 5 }} />
<Handle type="source" position={Position.Right} id="port-rt-source" style={{ ...rightTopPort, zIndex: 10 }} />
<Handle type="target" position={Position.Right} id="port-rt-target" style={{ ...rightTopPort, zIndex: 5 }} />
<Handle type="source" position={Position.Right} id="port-rb-source" style={{ ...rightBottomPort, zIndex: 10 }} />
<Handle type="target" position={Position.Right} id="port-rb-target" style={{ ...rightBottomPort, zIndex: 5 }} />
</div>
);
};
const TreeNode = ({ name, children }) => {
if (children && children.__type__ === 'component') {
const componentName = children.__name__;
const handleDragStart = (event) => {
event.dataTransfer.setData('application/reactflow', componentName);
event.dataTransfer.effectAllowed = 'move';
};
return (
<div className="component-leaf" draggable onDragStart={handleDragStart}>
🔷 {name}
</div>
);
}
const hasChildren = children && Object.keys(children).length > 0;
return (
<details>
<summary className="tree-folder">
<span style={{ wordBreak: 'break-all', whiteSpace: 'normal' }}>📂 {name}</span>
</summary>
{hasChildren &&
Object.entries(children).map(([childName, childData]) => (
<TreeNode key={childName} name={childName} children={childData} />
))
}
</details>
);
};
const LeftPanel = ({ library, treeKey, expanded, onToggle, treeRef, width }) => (
<aside style={{
width: width, background: '#f4f4f4', borderRight: '1px solid #ccc',
padding: 10, display: 'flex', flexDirection: 'column', height: '100%',
boxSizing: 'border-box', overflowY: 'auto'
}}>
<div className="left-block">
<div className="left-block-header">
<span>PDK Libraries</span>
<button className="toggle-btn" onClick={onToggle} title={expanded ? 'Collapse all' : 'Expand all'}>
{expanded ? '▾' : '▸'}
</button>
</div>
<div className="left-block-body" style={{ maxHeight: '45vh', overflowY: 'auto' }} key={treeKey} ref={treeRef}>
{library && Object.keys(library).length > 0 ? (
Object.entries(library).map(([key, value]) => (
<TreeNode key={key} name={key} children={value} />
))
) : (
<p style={{ color: '#999' }}>Loading library...</p>
)}
</div>
</div>
<div className="left-block">
<div className="left-block-header">Routing selections</div>
<div className="left-block-body">
<ul style={{ paddingLeft: 20, margin: 0 }}>
<li>Single mode wires</li>
<li>Multi-mode wires</li>
<li>DC electrical wires</li>
<li>RF electrical wires</li>
</ul>
</div>
</div>
<div className="left-block" style={{ marginTop: 'auto' }}>
<div className="left-block-header">User info</div>
<div className="left-block-body">
<div>Name: XXXXXX</div>
<div>ID: 12345678</div>
<button disabled>Log out</button>
</div>
</div>
</aside>
);
const RightPanel = memo(({ selectedNode, width, onRenameComponent }) => {
const [componentData, setComponentData] = useState(null);
const [loading, setLoading] = useState(false);
const [enlarged, setEnlarged] = useState(null);
const { setNodes } = useReactFlow();
const [editingComponentName, setEditingComponentName] = useState(false);
const [tempComponentName, setTempComponentName] = useState('');
const [localX, setLocalX] = useState('');
const [localY, setLocalY] = useState('');
const [localRotation, setLocalRotation] = useState('');
useEffect(() => {
const nodeId = selectedNode?.id;
if (!nodeId) {
setComponentData(null);
setLoading(false);
return;
}
const compName = selectedNode?.data?.componentName;
if (!compName) {
setComponentData(null);
setLoading(false);
return;
}
if (componentData && componentData.name === compName && componentData.nodeId === nodeId) return;
setLoading(true);
fetch(`/api/component/${encodeURIComponent(compName)}`)
.then(r => r.json())
.then(data => {
setComponentData({ ...data, nodeId: nodeId, componentDisplayName: selectedNode.data.componentDisplayName || data.name });
setLoading(false);
})
.catch(() => setLoading(false));
}, [selectedNode?.id, selectedNode?.data?.componentName, selectedNode?.data?.componentDisplayName]);
useEffect(() => {
if (selectedNode) {
setLocalX(selectedNode.position.x.toFixed(3));
setLocalY(selectedNode.position.y.toFixed(3));
setLocalRotation(((selectedNode.data?.rotation || 0)).toFixed(3));
}
}, [selectedNode?.position.x, selectedNode?.position.y, selectedNode?.data?.rotation, selectedNode?.id]);
const updatePosition = useCallback((id, axis, value) => {
const val = parseFloat(value);
if (isNaN(val)) return;
setNodes(nds => nds.map(n => n.id === id ? { ...n, position: { ...n.position, [axis]: val } } : n));
}, [setNodes]);
const updateRotation = useCallback((id, value) => {
const val = parseFloat(value);
if (isNaN(val)) return;
const clamped = Math.min(180, Math.max(-180, val));
setNodes(nds => nds.map(n => n.id === id ? { ...n, data: { ...n.data, rotation: clamped } } : n));
}, [setNodes]);
const formatPort = (port) => {
if (!port) return '—';
return `x:${port.x ?? '?'}, y:${port.y ?? '?'}, a:${port.a ?? '?'}, w:${port.width ?? '?'}`;
};
const currentRotation = selectedNode?.data?.rotation ?? 0;
const currentComponentDisplayName = selectedNode?.data?.componentDisplayName || '';
const handleStartEditName = () => {
setTempComponentName(currentComponentDisplayName);
setEditingComponentName(true);
};
const handleSaveName = () => {
const newName = tempComponentName.trim();
if (newName && selectedNode) {
onRenameComponent(selectedNode.id, newName);
}
setEditingComponentName(false);
};
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
handleSaveName();
} else if (e.key === 'Escape') {
setEditingComponentName(false);
}
};
return (
<aside style={{
width: width, background: '#fafafa', borderLeft: '1px solid #ccc',
padding: 10, display: 'flex', flexDirection: 'column', height: '100%',
boxSizing: 'border-box', overflowY: 'auto'
}}>
<div className="right-block">
<div className="right-block-header">Properties</div>
<div className="right-block-body">
{selectedNode ? (
<div>
<label>X:</label>
<input
type="number"
step="1"
value={localX}
onChange={(e) => setLocalX(e.target.value)}
onBlur={() => {
const val = parseFloat(localX);
if (!isNaN(val) && selectedNode) {
updatePosition(selectedNode.id, 'x', val);
setLocalX(val.toFixed(3));
} else if (selectedNode) {
setLocalX(selectedNode.position.x.toFixed(3));
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.currentTarget.blur();
}
}}
style={{ width: '100%' }}
/>
<br /><br />
<label>Y:</label>
<input
type="number"
step="1"
value={localY}
onChange={(e) => setLocalY(e.target.value)}
onBlur={() => {
const val = parseFloat(localY);
if (!isNaN(val) && selectedNode) {
updatePosition(selectedNode.id, 'y', val);
setLocalY(val.toFixed(3));
} else if (selectedNode) {
setLocalY(selectedNode.position.y.toFixed(3));
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.currentTarget.blur();
}
}}
style={{ width: '100%' }}
/>
<br /><br />
<label>A (deg):</label>
<div style={{ marginTop: 4 }}>
<input
type="number"
step="1"
value={localRotation}
onChange={(e) => setLocalRotation(e.target.value)}
onBlur={() => {
const val = parseFloat(localRotation);
if (!isNaN(val) && selectedNode) {
updateRotation(selectedNode.id, val);
setLocalRotation(val.toFixed(3));
} else if (selectedNode) {
setLocalRotation(((selectedNode.data?.rotation || 0)).toFixed(3));
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.currentTarget.blur();
}
}}
style={{ width: '100%', marginTop: 4 }}
/>
</div>
</div>
) : (
<p style={{ color: '#999' }}>Click a node to edit</p>
)}
</div>
</div>
{selectedNode?.data?.componentName && (
<div className="right-block">
<div className="right-block-header">Item details</div>
<div className="right-block-body">
{loading ? (
<p>Loading...</p>
) : componentData ? (
<>
<div style={{ marginBottom: '10px' }}>
<strong style={{ display: 'block', marginBottom: '4px' }}>Component name:</strong>
{editingComponentName ? (
<input
type="text"
value={tempComponentName}
onChange={(e) => setTempComponentName(e.target.value)}
onBlur={handleSaveName}
onKeyDown={handleKeyDown}
autoFocus
style={{ width: '100%', padding: '4px', fontSize: '0.85em', boxSizing: 'border-box' }}
/>
) : (
<div
style={{
cursor: 'pointer',
padding: '4px 6px',
backgroundColor: '#f0f0f0',
borderRadius: '3px',
display: 'inline-block',
wordBreak: 'break-all'
}}
onClick={handleStartEditName}
title="Click to edit"
>
{currentComponentDisplayName || componentData.name}
</div>
)}
</div>
<p style={{ wordBreak: 'break-all', whiteSpace: 'normal', overflowWrap: 'break-word', marginTop: '8px' }}>
<strong>PDK name:</strong><br />{componentData.name}
</p>
<p><strong>Description:</strong><br />
Foundry: {componentData.foundry}<br />
Process: {componentData.process}<br />
Year: {componentData.year}<br />
Designer: {componentData.designer}
</p>
<p><strong>PDK:</strong> (string)</p>
<p><strong>Ports:</strong></p>
<ul style={{ paddingLeft: 20, margin: 0 }}>
{componentData.ports && Object.entries(componentData.ports).map(([portName, portInfo]) => (
<li key={portName} style={{ letterSpacing: '0.5px' }}>{portName}: {formatPort(portInfo)}</li>
))}
</ul>
<p><strong>Image:</strong></p>
<div style={{
border: '1px dashed #ccc', width: '100%', height: 80,
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: '#999', background: '#fcfcfc', marginTop: 4,
overflow: 'hidden',
}}>
<img
src={`/api/component/${encodeURIComponent(componentData.name)}/image`}
alt="Component layout"
style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', cursor: 'pointer' }}
onClick={() => setEnlarged(`/api/component/${encodeURIComponent(componentData.name)}/image`)}
onError={(e) => {
e.currentTarget.style.display = 'none';
e.currentTarget.parentElement.innerHTML = 'No image available';
}}
/>
</div>
</>
) : (
<p style={{ color: '#999' }}>No data</p>
)}
</div>
</div>
)}
<div className="right-block" style={{ marginTop: 'auto' }}>
<div className="right-block-header">Function block to be explored</div>
<div className="right-block-body placeholder-block">Reserved for future functionality</div>
</div>
{enlarged && (
<div
style={{
position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
backgroundColor: 'rgba(0,0,0,0.8)', zIndex: 1000,
display: 'flex', alignItems: 'center', justifyContent: 'center',
cursor: 'zoom-out',
}}
onClick={() => setEnlarged(null)}
>
<img
src={enlarged}
alt="Enlarged layout"
style={{ maxWidth: '90%', maxHeight: '90%', objectFit: 'contain' }}
/>
</div>
)}
</aside>
);
}, (prevProps, nextProps) => {
const prev = prevProps.selectedNode;
const next = nextProps.selectedNode;
if (prev?.id !== next?.id) return false;
if (prev?.position?.x !== next?.position?.x) return false;
if (prev?.position?.y !== next?.position?.y) return false;
if (prev?.data?.rotation !== next?.data?.rotation) return false;
if (prev?.data?.componentName !== next?.data?.componentName) return false;
if (prev?.data?.componentDisplayName !== next?.data?.componentDisplayName) return false;
if (prevProps.width !== nextProps.width) return false;
return true;
});
const ResizeHandle = ({ onMouseDown }) => (
<div
onMouseDown={onMouseDown}
style={{
width: 6, cursor: 'col-resize', background: 'transparent',
transition: 'background 0.2s', zIndex: 5, flexShrink: 0,
}}
onMouseEnter={(e) => e.currentTarget.style.background = '#ccc'}
onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
/>
);
function App() {
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const reactFlowInstance = useReactFlow();
const [library, setLibrary] = useState(null);
const [treeKey, setTreeKey] = useState(0);
const [expanded, setExpanded] = useState(false);
const treeContainerRef = useRef(null);
const [leftWidth, setLeftWidth] = useState(240);
const [rightWidth, setRightWidth] = useState(220);
const [dragging, setDragging] = useState(null);
const [gridSnap, setGridSnap] = useState(false);
const componentCounterRef = useRef(1);
const generateComponentDisplayName = useCallback(() => {
const name = `component_${componentCounterRef.current}`;
componentCounterRef.current += 1;
return name;
}, []);
const renameComponent = useCallback((nodeId, newComponentDisplayName) => {
setNodes(nds => nds.map(n => {
if (n.id === nodeId) {
return {
...n,
data: {
...n.data,
componentDisplayName: newComponentDisplayName
}
};
}
return n;
}));
}, [setNodes]);
const fetchLibrary = useCallback(async () => {
try {
const res = await fetch('/api/library');
const data = await res.json();
setLibrary(data);
} catch (err) {
console.error('Failed to fetch library', err);
}
}, []);
useEffect(() => { fetchLibrary(); }, [fetchLibrary]);
const selectedNode = useMemo(() => nodes.find(n => n.selected), [nodes]);
const onDragOver = useCallback((event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);
const onDrop = useCallback((event) => {
event.preventDefault();
const type = event.dataTransfer.getData('application/reactflow');
if (!type) return;
const position = reactFlowInstance.screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
const componentDisplayName = generateComponentDisplayName();
const newNode = {
id: Date.now().toString(),
type: 'rotatableNode',
position,
data: {
label: type,
componentName: type,
rotation: 0,
componentDisplayName: componentDisplayName
},
};
setNodes((nds) => nds.concat(newNode));
}, [setNodes, reactFlowInstance, generateComponentDisplayName]);
const onConnect = useCallback((connection) => {
setEdges((eds) => addEdge({ ...connection, type: 'straight' }, eds));
}, [setEdges]);
const expandAll = useCallback(() => {
if (treeContainerRef.current) {
treeContainerRef.current.querySelectorAll('details').forEach(d => d.open = true);
}
}, []);
const collapseAll = useCallback(() => setTreeKey(k => k + 1), []);
const handleToggle = useCallback(() => {
if (expanded) { collapseAll(); setExpanded(false); }
else { expandAll(); setExpanded(true); }
}, [expanded, expandAll, collapseAll]);
const handleResizeStart = useCallback((side) => (e) => {
e.preventDefault();
setDragging(side);
}, []);
useEffect(() => {
if (!dragging) return;
const onMouseMove = (e) => {
if (dragging === 'left') {
setLeftWidth(Math.min(500, Math.max(150, e.clientX)));
} else if (dragging === 'right') {
const newWidth = window.innerWidth - e.clientX;
setRightWidth(Math.min(500, Math.max(150, newWidth)));
}
};
const onMouseUp = () => setDragging(null);
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
return () => {
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', onMouseUp);
};
}, [dragging]);
const toggleGridSnap = useCallback(() => {
setGridSnap(prev => !prev);
}, []);
return (
<div style={{ display: 'flex', width: '100%', height: '100%', userSelect: dragging ? 'none' : 'auto' }}>
<LeftPanel
library={library} treeKey={treeKey} expanded={expanded}
onToggle={handleToggle} treeRef={treeContainerRef} width={leftWidth}
/>
<ResizeHandle onMouseDown={handleResizeStart('left')} />
<div style={{ flex: 1, position: 'relative' }}>
<div style={{
position: 'absolute', top: 10, right: 10, zIndex: 10,
display: 'flex', alignItems: 'center', gap: 8,
}}>
<span style={{
fontSize: '0.85em', fontWeight: 'bold', fontFamily: "'Sofia Pro', sans-serif",
color: '#333', userSelect: 'none'
}}>Grid lock</span>
<div
onClick={toggleGridSnap}
style={{
width: 48, height: 24, borderRadius: 12,
background: gridSnap ? '#28a745' : '#ccc',
cursor: 'pointer', display: 'flex', alignItems: 'center',
padding: '0 2px', transition: 'background 0.3s',
boxShadow: 'inset 0 1px 3px rgba(0,0,0,0.2)',
}}
>
<div style={{
width: 20, height: 20, borderRadius: '50%',
background: '#fff', boxShadow: '0 1px 3px rgba(0,0,0,0.3)',
transform: gridSnap ? 'translateX(24px)' : 'translateX(0)',
transition: 'transform 0.2s',
}} />
</div>
</div>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onDragOver={onDragOver}
onDrop={onDrop}
onConnect={onConnect}
nodeTypes={{ rotatableNode: RotatableNode }}
fitView
snapToGrid={gridSnap}
snapGrid={[10, 10]}
nodesDraggable={true}
nodesConnectable={true}
elementsSelectable={true}
connectionRadius={50}
>
<Controls />
<Background />
</ReactFlow>
</div>
<ResizeHandle onMouseDown={handleResizeStart('right')} />
<RightPanel
selectedNode={selectedNode}
width={rightWidth}
onRenameComponent={renameComponent}
/>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ReactFlowProvider>
<App />
</ReactFlowProvider>
);
</script>
</body>
</html>
{% endraw %}
-15
View File
@@ -61,21 +61,6 @@ assert(
'save-layout response should include an svg_url for the new layout tab'
);
assert(
<<<<<<< HEAD
serverPy.includes('svg_ready') &&
serverPy.includes('svg_version') &&
serverPy.includes('file_version(svg_path)') &&
serverPy.includes("url_for('get_layout_svg', project_name=project, cell_name=cell, v=svg_version)"),
'save-layout response should only expose a versioned SVG URL after the preview file is ready'
);
assert(
serverPy.includes('temp_svg_path') &&
serverPy.includes('os.replace(temp_svg_path, svg_path)'),
'save-layout should publish generated SVG previews atomically instead of serving partially written files'
);
assert(
=======
>>>>>>> jingwen_main
serverPy.includes('RouterStackUnavailable') &&
serverPy.includes('except RouterStackUnavailable as e') &&
serverPy.includes('"preview_status": preview_status') &&
-10
View File
@@ -33,16 +33,6 @@ assert(
'Build Layout should use the backend svg_url response'
);
assert(
<<<<<<< HEAD
canvasHtml.includes('result.svg_ready && result.svg_url') &&
canvasHtml.includes('buildLayoutRequestRef') &&
canvasHtml.includes('buildLayoutBusyRef') &&
canvasHtml.includes("cache: 'no-store'"),
'Build Layout should wait for a ready, versioned SVG response and prevent stale duplicate preview updates'
);
assert(
=======
>>>>>>> jingwen_main
canvasHtml.includes('result.preview_error') &&
canvasHtml.includes('Preview skipped: '),
'Build Layout should log when the backend saves YAML but skips SVG preview because the router stack is unavailable'