25 Commits

Author SHA1 Message Date
PotatoMaxwell bf8e72f5b6 Merge pull request 'update canvas.html' (#8) from jingwen_main into qinyue_main
Reviewed-on: #8
2026-06-08 05:20:38 +00:00
= 1b7357e419 redundent files deleted 2026-06-04 20:01:32 +08:00
= b67c647995 Merged from jingwen_main for image icon revision 2026-06-04 20:00:13 +08:00
= 7ac76aaee9 Layout refresh latency bug revised 2026-06-04 19:47:18 +08:00
= f9c3f12aea gitignore added 2026-06-04 17:10:55 +08:00
= 84d43d1ef4 .gitignore added 2026-06-04 17:03:04 +08:00
= 392e6d655e Removing mxpic_forge from the dependncy 2026-06-01 13:18:57 +08:00
root 7cf618fe02 Programm architecture simplified so that the EDA can now run individually without generation of .gds file 2026-06-01 09:19:44 +08:00
root 78f38d3be7 docs: design required mxpic_router stack 2026-05-31 23:32:25 +08:00
root ce7f6e95c4 Routing problem for multi-pin port and anchors are debugged 2026-05-31 22:16:44 +08:00
root 9b4e8da796 Updated with more bug revise. The login page and dashboard is also changedd 2026-05-31 10:14:50 +08:00
root e3f708a1a7 routing for canvase level object is finished. 2026-05-30 16:37:37 +08:00
root bf223b52ac More annotation added to the program 2026-05-30 12:44:44 +08:00
root b3f29398f0 Annotations added 2026-05-30 12:23:51 +08:00
root 5a3a80700f 1. Anchor routing added with mutiport 2026-05-30 12:04:02 +08:00
root 2d9b2b0983 routing link crossover violation added 2026-05-29 23:47:26 +08:00
root 80d7514740 auto-renaming process changed to abbreviation names with catagoreis, like DC, MMI and PD... 2026-05-29 23:42:10 +08:00
root 07ee7f9dd7 Routing nchor added 2026-05-29 21:51:57 +08:00
root 1215bf978a CODEX revised with following function: 1. GDS building, 2. different user group with different authority. 2026-05-28 20:35:49 +08:00
root e6e9e13cf2 Updated 2026-05-28 17:53:41 +08:00
root 48555f5686 Basic element added: anchor and port 2026-05-28 11:56:02 +08:00
root 1c2a0647cb Multiuser system initiated with log recording 2026-05-27 14:27:31 +08:00
root 2b4bb5ea64 System updated with many bug revised 2026-05-27 11:40:32 +08:00
root 83b606c6c3 updated using CODEX, refresh in canvas layout and functionalities 2026-05-27 10:59:05 +08:00
root 809f73c1f5 updated with github 2026-05-26 11:55:56 +08:00
22 changed files with 678 additions and 1629 deletions
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+19 -3
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, 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, 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
# Developer : Qin Yue @ 2026
# Organization : OptiHK Limited
# -----------------------------------------------------------------------------
@@ -9,6 +9,7 @@ import os
import re
import shutil
import json
import uuid
import yaml
from collections import OrderedDict
from functools import wraps
@@ -135,6 +136,12 @@ 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")
@@ -739,24 +746,31 @@ 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,
svg_path,
temp_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({
@@ -765,7 +779,9 @@ 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) if svg_path else None,
"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,
"preview_status": preview_status,
"preview_error": preview_error
}), 200
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 319 KiB

@@ -0,0 +1,211 @@
# =============================================
# 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,4 +1,90 @@
<?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">
@@ -67,5 +153,6 @@
<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: 15 KiB

After

Width:  |  Height:  |  Size: 79 KiB

@@ -13,6 +13,16 @@ 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
@@ -86,6 +96,7 @@ 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
@@ -93,10 +104,17 @@ 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
@@ -104,6 +122,9 @@ 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
@@ -129,9 +150,20 @@ 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,22 +1237,6 @@ ${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;
@@ -1292,7 +1276,6 @@ ${linksYaml}`;
createComponentSymbolMetrics,
transformPortInfo,
getNodePortCanvasPoint,
getRotatableNodeHandleDirection,
buildPortHandles,
buildElementPorts,
buildElementPinEntries,
+11 -30
View File
@@ -1566,7 +1566,6 @@ Organization : OptiHK Limited
calculateLayoutBounds,
calculateCompositeBoxSize,
buildPortHandles,
getRotatableNodeHandleDirection,
buildElementPorts,
getElementPinName,
buildElementBoxSize,
@@ -1699,10 +1698,8 @@ 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]);
@@ -1722,16 +1719,6 @@ 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(
() => {
@@ -1857,28 +1844,24 @@ Organization : OptiHK Limited
transformOrigin: 'center center',
pointerEvents: 'none'
}}>
{portHandles.map((portHandle) => {
const originalDir = portDirectionMap.get(portHandle.name) || portHandle.position;
const effectiveDir = rotateHandleDirection(originalDir, data.rotation || 0);
return (
{portHandles.map((portHandle) => (
<React.Fragment key={portHandle.name}>
<Handle
type="source"
position={handlePositionMap[effectiveDir]}
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]}
id={portHandle.name}
title={portHandle.name}
style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 10, pointerEvents: 'all' }}
/>
<Handle
type="target"
position={handlePositionMap[effectiveDir]}
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]}
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}
@@ -4024,10 +4007,8 @@ 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)
|| getRotatableNodeHandleDirection(nodeMap[edge.source], edge.sourceHandle);
const targetDirection = getAnchorHandleRouteDirection(nodeMap[edge.target], edge.targetHandle)
|| getRotatableNodeHandleDirection(nodeMap[edge.target], edge.targetHandle);
const sourceDirection = getAnchorHandleRouteDirection(nodeMap[edge.source], edge.sourceHandle);
const targetDirection = getAnchorHandleRouteDirection(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
@@ -4050,7 +4031,7 @@ Organization : OptiHK Limited
};
});
return [...separatedEdges, ...rulerEdges];
}, [currentEdges, currentNodes, getAnchorHandleRouteDirection, getRotatableNodeHandleDirection, rulerEdges]);
}, [currentEdges, currentNodes, getAnchorHandleRouteDirection, rulerEdges]);
const [projectCompositeMap, setProjectCompositeMap] = useState({});
const [standaloneComposites, setStandaloneComposites] = useState([]);
@@ -6037,7 +6018,6 @@ 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,
@@ -6047,8 +6027,9 @@ 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;
@@ -6062,7 +6043,7 @@ Organization : OptiHK Limited
: p
)));
addLog(`Connected ${connection.sourceHandle} to ${connection.targetHandle}.`);
}, [activePageId, activePage, rulerMode, currentLinkRoute, technologyManifest, addLog, getAnchorHandleRouteDirection]);
}, [activePageId, activePage, rulerMode, currentLinkRoute, technologyManifest, addLog]);
// Select custom route edges from their SVG hit target.
const handleRouteEdgeMouseDown = useCallback((event) => {
-829
View File
@@ -1,829 +0,0 @@
<!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
@@ -1,750 +0,0 @@
<!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,6 +61,21 @@ 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,6 +33,16 @@ 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'