← Commit history
BRIDGE_VERSIONadded+1
@@ -0,0 +1 @@+0.9.0
CONTRIBUTING.mdadded+75
@@ -0,0 +1,75 @@+# Contributing++This bridge is part of the [Adom Desktop](https://github.com/adom-inc/adom-desktop) bridge ecosystem. It lives in its own repo (split from the main monorepo in 2026-05) specifically so the community can fork and improve it without coordinating with Adom Desktop's release cycle.++## Two-way street++- **You can extend this bridge** — add support for a new app version, new verbs, new platforms — and PR it back here. We sync accepted PRs into adom-desktop's bundled copy on the next release.+- **You can fork it** if your changes diverge too much (e.g. private vendor SDK integration). The bridge.json schema is the contract; fork freely under MIT.++## How shipping works++1. **Develop locally.** Clone this repo, edit, test. The bridge runs alongside an installed [Adom Desktop](https://github.com/adom-inc/adom-desktop); use `adom-desktop bridge_install` with a local `file://` manifest URL to test your changes against a real GUI.+2. **Open a PR** against `main`. The CI in `.github/workflows/validate.yml` checks that `bridge.json` is valid + verb prefixes look right + the BRIDGE_VERSION was bumped vs. main.+3. **Maintainer reviews + merges.** No squash-merge — we preserve your authored commits.+4. **Maintainer tags + ships.** A tag `v<X.Y.Z>` triggers `release-bridge.sh` on a maintainer's box, which uploads the new zip + manifest to [the Adom Wiki](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges). Anyone running Adom Desktop v1.7.19+ picks it up within 4 hours or on the next `adom-desktop refresh_bridges`.++We DON'T auto-ship from GitHub Actions today. Reason: `release-bridge.sh` needs an Adom Wiki API token, and we keep that secret on maintainer machines rather than in GH Actions secrets. The community trust model around auto-publishing community PRs to a shared wiki space is part of a separate effort ("Chunk D" in adom-desktop's TODO.md) — comment on an issue if you want to help design that.++## Bridge.json schema++See [the SDK guide on the Adom Wiki](https://wiki-ufypy5dpx93o.adom.cloud/apps/adom-desktop-bridges) for the authoritative schema. Quick summary:++- `manifest_version: 1`+- `name` — must match the directory name in adom-desktop's `plugins/` (when bundled)+- `version` — semver, bumped on every PR that ships+- `spawn.port: 0` — **always use 0** as of Adom Desktop v1.8.31+. Adom Desktop picks an OS-assigned ephemeral port and passes it via `--port <N>` to your bridge process. Don't hardcode.+- `spawn.healthEndpoint: "/status"` — path-only (recommended). Adom Desktop builds the URL using the runtime port.+- `verbPrefixes: ["<name>_"]` — the CLI verb prefixes this bridge owns.++## Versioning++- **Patch** (`0.8.3 → 0.8.4`): bug fixes, hardening, no schema or behavior changes.+- **Minor** (`0.8 → 0.9`): new verbs, new optional manifest fields, backward-compatible behavior changes.+- **Major** (`0.x → 1.0`, `1.x → 2.0`): breaking changes to verb shapes or removal of verbs. Coordinate with adom-desktop maintainers before submitting — we may need to wait for a desktop-app release that handles both versions.++Bump `BRIDGE_VERSION` AND `bridge.json`'s `"version"` field in the same commit — they must match.++## Testing locally++```bash+# 1. Clone this repo somewhere+git clone https://github.com/adom-inc/adom-bridge-<NAME>.git+cd adom-bridge-<NAME>++# 2. Install adom-desktop, then point it at this local manifest:+adom-desktop bridge_install '{"manifestUrl":"file:///path/to/adom-bridge-<NAME>/bridge.json"}'++# Note: file:// works only when manifestUrl is parsed by adom-desktop's CLI on the+# same machine that owns the bridge files. For Docker callers, push to a wiki staging+# area and use the wiki URL.++# 3. Run any verb the bridge defines:+adom-desktop <name>_<verb> '{"...":"..."}'++# 4. Run the SDK demo to verify install/list/invoke/uninstall lifecycle:+bash scripts/demo-thirdparty-bridge.sh  # in adom-desktop repo+```++## Why this bridge lives in its own repo++When adom-desktop was a monorepo, all bridges shipped on adom-desktop's NSIS release cycle — meaning a community contributor couldn't release a KiCad fix without waiting for an adom-desktop maintainer to cut a new desktop installer. As of 2026-05 the bridges live in independent repos so:++- Community can release at their own cadence+- Adom Desktop's installer ships a "known-good" snapshot but auto-updates from the wiki for newer versions+- A maintainer outside Adom Industries can take over a bridge (e.g. a KiCad 11 expert maintains the KiCad bridge) without commit access to adom-desktop itself++See [adom-desktop TODO.md → Chunks C/D](https://github.com/adom-inc/adom-desktop/blob/main/TODO.md) for the broader design.++## Code of conduct++Be kind. We're building this for engineers who need their CAD tools to work with AI without giving up local control of their files. If your PR is rejected, we'll explain why — and you can fork.++## License++MIT — see [LICENSE](LICENSE).
KICAD-PLUGIN-PROMPT.mdadded+265
@@ -0,0 +1,265 @@+# Build the Adom Desktop KiCad Plugin++> **Historical design doc — captured from the original brief that bootstrapped this plugin. Terminology here predates the current naming: what's now called "Adom Desktop" was originally called the "Adom Desktop" (and the WebSocket relay was the "Adom Relay Server"). The implementation has since shipped; this doc is kept as design provenance, not as current API reference. For the live API, see [`SKILL.md`](SKILL.md) and the bridge SDK at <https://wiki.adom.inc/adom/adom-desktop-bridges>.**++## What We're Building++A KiCad plugin (VS Code extension or standalone) that integrates KiCad with the Adom platform. The architecture has three layers:++1. **Adom Relay Server** — a WebSocket server running in a remote Docker container (already built, you don't touch this)+2. **Adom Desktop** — a Tauri app running on this Windows desktop (already built, this is the bridge)+3. **KiCad Plugin** — what you're building. It receives commands from the Adom Desktop and controls KiCad++The flow is:+```+Cloud (Claude Code) → Adom Relay Server (Docker, WebSocket) → Adom Desktop (Tauri, Windows) → KiCad Plugin → KiCad+```++## How Commands Reach the Plugin++The Adom Desktop (Tauri app, already running) receives `command` messages from the WebSocket server with `"app": "kicad"`. Currently, the Adom Desktop doesn't know how to execute KiCad commands — it needs your plugin to handle them.++There are two architecture options for how the Adom Desktop talks to the KiCad Plugin:++### Option A: KiCad Action Plugin (Python, runs inside KiCad)+- A Python plugin that registers as a KiCad "Action Plugin"+- Runs a local HTTP or WebSocket server inside KiCad's Python environment+- The Adom Desktop connects to this local server to send commands+- Has full access to KiCad's Python scripting API (pcbnew, eeschema)+- KiCad 9 also has an IPC API (JSON-RPC over Unix socket / named pipe)++### Option B: External automation via KiCad CLI + IPC+- A standalone program (Node.js, Python, or Rust) that the Adom Desktop invokes+- Uses `kicad-cli` for file operations and KiCad 9's IPC API for controlling the running instance+- Doesn't require a plugin installed in KiCad itself+- May have less control over the running KiCad UI++### Recommended: Hybrid approach+Use KiCad 9's IPC API when KiCad is running (for interactive control like opening files, navigating), and fall back to `kicad-cli` for batch operations (DRC, export, library management).++## KiCad Commands to Support++These are the commands the Adom Desktop will forward to your plugin. Each arrives as a JSON message:++```json+{+  "app": "kicad",+  "command": "<command_name>",+  "args": { ... }+}+```++### 1. `open_board`+Open a PCB board file in KiCad's PCB editor.+```json+{ "command": "open_board", "args": { "filePath": "C:\\Users\\john\\project\\board.kicad_pcb" } }+```+Expected response: `{ "success": true }` or `{ "success": false, "error": "File not found" }`++### 2. `open_schematic`+Open a schematic file in KiCad's schematic editor.+```json+{ "command": "open_schematic", "args": { "filePath": "C:\\Users\\john\\project\\schematic.kicad_sch" } }+```++### 3. `install_library`+Install a symbol, footprint, or 3D model library into KiCad.+```json+{+  "command": "install_library",+  "args": {+    "libraryPath": "C:\\Users\\john\\libs\\adom-test.kicad_sym",+    "libraryType": "symbol",+    "libraryName": "Adom Test Symbols"+  }+}+```+`libraryType` is one of: `"symbol"`, `"footprint"`, `"3dmodel"`+`libraryName` is optional — use the filename if not provided.++For symbol libraries, add to KiCad's symbol library table (`sym-lib-table`).+For footprint libraries, add to the footprint library table (`fp-lib-table`).++### 4. `run_drc`+Run a Design Rule Check on a PCB board file.+```json+{ "command": "run_drc", "args": { "filePath": "C:\\Users\\john\\project\\board.kicad_pcb" } }+```+If `filePath` is omitted, run DRC on the currently open board.++Expected response includes violations:+```json+{+  "success": true,+  "violations": [+    {+      "severity": "error",+      "message": "Clearance violation (0.15mm < 0.2mm)",+      "location": "U1 pad 3"+    }+  ]+}+```++## File Transfer Context++Before KiCad commands arrive, the Adom Desktop may receive `send_files` messages containing KiCad files (symbols, footprints, board files). These are base64-decoded and saved to a local directory. The subsequent `open_board`, `open_schematic`, or `install_library` command will reference the saved file path.++The Adom Desktop handles file saving — your plugin just needs to work with local file paths.++## KiCad 9 IPC API Reference++KiCad 9 introduced a JSON-RPC IPC API for external control:++- **Transport**: Unix domain socket (Linux/macOS) or named pipe (Windows)+- **Socket path (Windows)**: `\\.\pipe\kicad-<pid>` or discoverable via KiCad's lock files+- **Protocol**: JSON-RPC 2.0+- **Available when**: KiCad is running with an open project++Key IPC capabilities:+- Open/close documents+- Get board/schematic data+- Run DRC/ERC+- Place components, draw traces+- Export files (Gerber, PDF, SVG)++Check KiCad's developer documentation for the full IPC API reference:+https://dev-docs.kicad.org/en/apis-and-binding/ipc/++## KiCad Python Scripting API++If building a KiCad Action Plugin, you have access to:++```python+import pcbnew  # PCB editor API++# Get current board+board = pcbnew.GetBoard()++# Access footprints, tracks, zones, etc.+for footprint in board.GetFootprints():+    ref = footprint.GetReference()+    pos = footprint.GetPosition()++# Run DRC+drc = pcbnew.DRC()+drc.Run()+markers = board.GetMarkers()+```++For the schematic editor, KiCad 9 has `eeschema` Python bindings, though they're more limited.++## Integration with the Adom Desktop++The Adom Desktop (Tauri app) needs a way to call your plugin. Define a clear interface:++### Option 1: Local HTTP Server (recommended for Action Plugin)+Your KiCad plugin starts a local HTTP server (e.g., port 8772) that accepts POST requests:++```+POST http://localhost:8772/command+Content-Type: application/json++{+  "command": "open_board",+  "args": { "filePath": "C:\\..." }+}+```++Response:+```json+{ "success": true }+```++The Adom Desktop would be updated to forward KiCad commands to `http://localhost:8772/command`.++### Option 2: CLI Wrapper+A command-line tool that the Adom Desktop invokes:+```+adom-kicad-bridge open_board --file "C:\Users\john\project\board.kicad_pcb"+```++### Option 3: Direct KiCad CLI+For some operations, use `kicad-cli` directly:+```+kicad-cli pcb drc --output report.json board.kicad_pcb+kicad-cli sym export svg --output /tmp/sym.svg symbol.kicad_sym+```++## What to Build First (MVP)++1. **Detect KiCad installation** — find the KiCad executable and version on Windows+2. **Open files** — implement `open_board` and `open_schematic` by launching KiCad with the file path, or via IPC if KiCad is already running+3. **Install library** — modify KiCad's `sym-lib-table` or `fp-lib-table` to add new libraries+4. **Run DRC** — use `kicad-cli pcb drc` to run DRC and parse results, or use IPC API++## Environment Details++- **OS**: Windows (the desktop machine is "AdomLapper", Windows, x86_64)+- **KiCad version**: Should detect automatically, but targeting KiCad 9.x+- **Adom Desktop**: Tauri 2.10 app, already connects to the cloud relay server+- **This VS Code instance**: Running on the Windows desktop alongside KiCad++## Sample KiCad Symbol for Testing++Here's a simple test symbol (ADOM_MCU, 8-pin) you can use to test library installation:++```+(kicad_symbol_lib+  (version 20231120)+  (generator "adom")+  (symbol "ADOM_MCU"+    (pin_names (offset 1.016))+    (exclude_from_sim no)+    (in_bom yes)+    (on_board yes)+    (property "Reference" "U"+      (at 0 10.16 0)+      (effects (font (size 1.27 1.27)))+    )+    (property "Value" "ADOM_MCU"+      (at 0 -10.16 0)+      (effects (font (size 1.27 1.27)))+    )+    (property "Footprint" ""+      (at 0 0 0)+      (effects (font (size 1.27 1.27)) hide)+    )+    (property "Datasheet" ""+      (at 0 0 0)+      (effects (font (size 1.27 1.27)) hide)+    )+    (property "Description" "Adom Test MCU Symbol"+      (at 0 0 0)+      (effects (font (size 1.27 1.27)) hide)+    )+    (symbol "ADOM_MCU_0_1"+      (rectangle+        (start -7.62 8.89)+        (end 7.62 -8.89)+        (stroke (width 0.254) (type default))+        (fill (type background))+      )+    )+    (symbol "ADOM_MCU_1_1"+      (pin input line (at -10.16 6.35 0) (length 2.54) (name "VCC") (number "1"))+      (pin input line (at -10.16 3.81 0) (length 2.54) (name "GND") (number "2"))+      (pin bidirectional line (at -10.16 1.27 0) (length 2.54) (name "SDA") (number "3"))+      (pin bidirectional line (at -10.16 -1.27 0) (length 2.54) (name "SCL") (number "4"))+      (pin output line (at 10.16 6.35 180) (length 2.54) (name "TX") (number "5"))+      (pin input line (at 10.16 3.81 180) (length 2.54) (name "RX") (number "6"))+      (pin bidirectional line (at 10.16 1.27 180) (length 2.54) (name "GPIO0") (number "7"))+      (pin bidirectional line (at 10.16 -1.27 180) (length 2.54) (name "GPIO1") (number "8"))+    )+  )+)+```++## Success Criteria++When done, the cloud Claude Code should be able to:+1. Call `kicad_open_board` via MCP → Adom Relay Server → Adom Desktop → your plugin → KiCad opens the file+2. Call `kicad_install_library` → a new symbol library appears in KiCad's library manager+3. Call `kicad_run_drc` → DRC results come back to Claude Code in the cloud++Start by figuring out the best way to detect and communicate with KiCad on this Windows machine, then build up from there.
LICENSEadded+21
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2026 Adom Industries, Inc. and contributors++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
README.md+369−132
@@ -1,189 +1,426 @@-# Adom Desktop - KiCad Bridge+# Adom KiCad Bridge Plugin -Drive KiCad 7/8/9/10 end-to-end from the cloud: install a custom symbol and footprint, place them on a schematic and board, render in 3D, and export Gerbers — the user's laptop is never touched by hand.+A Python-based bridge server that enables the Adom Desktop to control KiCad 9 on Windows. It receives commands over HTTP and translates them into KiCad operations — opening/closing editors, installing libraries, loading specific symbols and footprints, viewing 3D models, and running design rule checks. -![A custom symbol opened in the KiCad symbol editor](kc-symbol.png)+## Architecture -![The part placed on the schematic sheet](kc-schematic.png)+```+Cloud (Claude Code)+  | WebSocket+Adom Relay Server (Docker)+  | WebSocket+Adom Desktop (Tauri, this machine)+  | HTTP POST to localhost:8772+KiCad Bridge Server (this plugin)+  | subprocess / Win32 API / file manipulation+KiCad 9 (project manager, symbol editor, footprint editor, 3D viewer, pcbnew, eeschema, kicad-cli)+```++The bridge server runs on `http://127.0.0.1:8772` and exposes two endpoints:++- **`GET /health`** — Returns KiCad detection info and server status+- **`POST /command`** — Accepts a JSON command and returns a JSON result++## Requirements++- Python 3.10+ (system Python or KiCad's bundled Python)+- KiCad 9.x installed on Windows+- No pip dependencies — uses Python stdlib only++## Quick Start++```bash+# From the project root+python plugins/kicad/server.py++# Or use the batch launcher (auto-finds Python)+plugins\kicad\start.bat+```++## Commands++All commands are sent as `POST /command` with JSON body `{"command": "<name>", "args": {...}}`.+All responses are JSON with `{"success": bool, "output": "...", ...}`.++### `install_library`++Installs a symbol or footprint library file into KiCad's user directory and registers it in the global library table.++**Symbol library:**++```json+{+  "command": "install_library",+  "args": {+    "libraryPath": "C:/path/to/library.kicad_sym",+    "libraryType": "symbol",+    "libraryName": "My Symbols",+    "description": "Custom symbol library"+  }+}+```++**Footprint library (with optional 3D model):**++```json+{+  "command": "install_library",+  "args": {+    "libraryPath": "C:/path/to/footprint.kicad_mod",+    "libraryType": "footprint",+    "libraryName": "My Footprints",+    "description": "Custom footprint library",+    "model3dPath": "C:/path/to/model.wrl"+  }+}+```++| Arg | Required | Description |+|-----|----------|-------------|+| `libraryPath` | yes | Absolute path to `.kicad_sym`, `.kicad_mod`, or `.pretty` directory |+| `libraryType` | yes | `"symbol"` or `"footprint"` |+| `libraryName` | no | Display name in KiCad (defaults to filename stem) |+| `description` | no | Description shown in KiCad's library manager |+| `model3dPath` | no | Path to 3D model file (footprint only; `.wrl`, `.step`, etc.) |++**How it works:**++- **Symbols**: Copies `.kicad_sym` to `~/Documents/KiCad/9.0/symbols/` and adds an entry to `sym-lib-table`+- **Footprints**: Creates a `.pretty` directory in `~/Documents/KiCad/9.0/footprints/`, copies the `.kicad_mod` inside, and adds an entry to `fp-lib-table`. Multiple footprints can be added to the same library.+- **3D models**: If `model3dPath` is provided, copies the model to `~/Documents/KiCad/9.0/3dmodels/<libraryName>.3dshapes/` and inserts a `(model ...)` reference into the footprint file+- Rejects duplicate library names (but allows adding footprints to existing libraries)+- **Important**: Libraries installed while KiCad is running won't be visible until KiCad restarts. Use `close_kicad` first if KiCad is already open.++### `open_symbol_editor` -![The finished board in the 3D viewer](kc-3d-board.png)+Opens the KiCad Symbol Editor and optionally navigates to and loads a specific symbol. If KiCad is not running, it will be launched automatically. -## Source code + contributing (2026-05)+```json+{+  "command": "open_symbol_editor",+  "args": {+    "symbolName": "ADOM_MCU",+    "libraryName": "Adom Test Symbols"+  }+}+``` -The KiCad bridge moved out of `adom-inc/adom-desktop`'s monorepo into its own dedicated repo so the community can extend it without coordinating with Adom Desktop's release cycle.+| Arg | Required | Description |+|-----|----------|-------------|+| `symbolName` | no | Symbol to navigate to and load in the editor canvas |+| `libraryName` | no | Library containing the symbol (used as fallback search text) | -### Where the source lives+- Sends Win32 `WM_COMMAND` to the KiCad project manager to open the Symbol Editor+- If `symbolName` is provided, uses automated search + keyboard shortcuts to load the symbol+- If the requested symbol is already loaded, the editor is brought to the foreground+- Requires the library to be installed first via `install_library`+- Uses a PowerShell helper script (`navigate_symbol.ps1`) for Win32 UI automation -The canonical repo is **[`adom-inc/adom-bridge-kicad`](https://github.com/adom-inc/adom-bridge-kicad)** on GitHub. It's private today and will go public after a review pass.+### `open_footprint_editor` -In the meantime, the full source — code, history, contributing guide, license — is **mirrored on this wiki page**:+Opens the KiCad Footprint Editor and optionally navigates to and loads a specific footprint. If KiCad is not running, it will be launched automatically. -| Download | What it is | How to use |-|---|---|---|-| [`adom-bridge-kicad-source.tar.gz`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop-kicad-bridge/adom-bridge-kicad-source.tar.gz) | Clean source tree, no `.git` directory. ~165 KB. | `tar xzf adom-bridge-kicad-source.tar.gz && cd adom-bridge-kicad/` |-| [`adom-bridge-kicad.bundle`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop-kicad-bridge/adom-bridge-kicad.bundle) | Git bundle with full commit history. ~240 KB. | `git clone https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop-kicad-bridge/adom-bridge-kicad.bundle adom-bridge-kicad && cd adom-bridge-kicad && git log` |+```json+{+  "command": "open_footprint_editor",+  "args": {+    "footprintName": "ADOM_R0805",+    "libraryName": "My Footprints"+  }+}+``` -No GitHub account needed to download either — both URLs are public.+| Arg | Required | Description |+|-----|----------|-------------|+| `footprintName` | no | Footprint to navigate to and load in the editor canvas |+| `libraryName` | no | Library containing the footprint (used as fallback search text) | -### How to contribute+- KiCad 9's launcher buttons use dynamically-assigned WM_COMMAND IDs that are unreliable, so this handler uses a mouse-click approach: it finds the "Footprint Editor" label in the project manager, locates the adjacent icon panel (~51x51 px), and clicks it+- If `footprintName` is provided, uses the same PowerShell navigation script as the Symbol Editor+- If the requested footprint is already loaded, the editor is brought to the foreground -We accept community improvements. Most useful targets right now:+### `open_3d_viewer` -- **KiCad 11 support** — when KiCad 11 ships, the `kicad_detect.py` discovery + the in-process bridge plugin both need updates. Fork-friendly.-- **macOS / Linux ports** — bridge currently Windows-only; cross-platform PRs welcome.-- **New verbs** — anything KiCad exposes via `pcbnew.*` / `kicad-cli` that you'd find useful.+Opens KiCad's 3D Viewer. Works from either the Footprint Editor or the PCB Editor (pcbnew). -Two contribution paths, depending on your access:+```json+{+  "command": "open_3d_viewer",+  "args": {}+}+``` -**With a GitHub account (preferred):** wait until we make the repo public, then fork on GitHub and open a PR. We're aiming to flip the repo public in the next maintenance pass.+- **From Footprint Editor**: sends `WM_COMMAND 20500` (View > 3D Viewer)+- **From PCB Editor (pcbnew)**: sends `Alt+3` keystroke (View > 3D Viewer)+- Tries Footprint Editor first, then PCB Editor+- Requires a footprint/board to be loaded for the 3D model to display -**Without GitHub access (patch-by-email-style, available right now):** Clone the bundle, make your changes, generate a patch with `git format-patch origin/main..HEAD --stdout > my-changes.patch`, and email it to **`[email protected]`**. Adom maintainers will review and apply.+### `close_symbol_editor` -See `CONTRIBUTING.md` inside the tarball/bundle for the full PR + ship workflow.+Closes the KiCad Symbol Editor window. The KiCad project manager remains open. -### How shipping works after merge+```json+{+  "command": "close_symbol_editor",+  "args": {}+}+```++- Sends `WM_CLOSE` to the Symbol Editor window+- Idempotent: returns success even if the Symbol Editor is not open+- If the symbol has unsaved changes, KiCad will prompt the user to save -1. Maintainer merges your PR into `main`-2. Maintainer tags `v<X.Y.Z>` + runs `bash scripts/release-bridge.sh kicad` on their box-3. New zip + manifest published to this wiki page (replaces current version)-4. Every Adom Desktop install running v1.7.19+ auto-picks up your change within 4 hours OR immediately via `adom-desktop refresh_bridges`+### `close_footprint_editor` -No NSIS installer rebuild required. Your code reaches real users in ~30s after merge.+Closes the KiCad Footprint Editor window. Also closes the 3D Viewer if it was opened from the Footprint Editor. The KiCad project manager remains open.++```json+{+  "command": "close_footprint_editor",+  "args": {}+}+``` -### License+- Idempotent: returns success even if the Footprint Editor is not open -MIT — see the `LICENSE` file in the tarball.+### `close_3d_viewer` ----+Closes the KiCad 3D Viewer window. The Footprint Editor remains open. +```json+{+  "command": "close_3d_viewer",+  "args": {}+}+``` -The KiCad bridge is one of three reference implementations bundled with Adom Desktop. It's the most complex of the three — a multi-instance Python server that runs alongside KiCad GUI processes and provides both a forward path (CLI → bridge → kicad-cli) and a reverse path (CLI → bridge → in-process Python plugin inside KiCad).+- Idempotent: returns success even if the 3D Viewer is not open -If you're authoring a new third-party bridge, see the [bridge SDK guide](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges) first. Use this page for architectural reference once you've grasped the basics.+### `close_kicad` -## Quick facts+Closes all KiCad windows — editors and viewers first, then the project manager. -| | |-|---|---|-| Name | `kicad` |-| Display | KiCad EDA |-| Spawn kind | Python (`server.py`) |-| Port | 8772 |-| Verb prefix | `kicad_*` (23 verbs) |-| Watcher | yes (`*.kicad_pcb`, `*.kicad_sch`, `*.kicad_pro`) |-| Source zip | [`kicad-bridge-v0.8.2.zip`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-bridge-v0.8.2.zip) |-| Manifest | [`kicad-bridge-manifest.json`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-bridge-manifest.json) |-| User-facing skill | [`kicad-SKILL.md`](https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-SKILL.md) |+```json+{+  "command": "close_kicad",+  "args": {}+}+``` -## What it does+- Closes Symbol Editor, Footprint Editor, 3D Viewer, and other editor windows before the project manager+- Idempotent: returns success even if KiCad is not running+- If any editor has unsaved changes, KiCad will prompt the user to save+- Useful before `install_library` to ensure newly installed libraries are visible on next launch -- **Forward path** (CLI → bridge → kicad-cli): board/schematic linting (`kicad_lint_board`, `kicad_lint_schematic`, `kicad_lint_library`), DRC/ERC (`kicad_run_drc`, `kicad_run_erc`), format upgrades, exports.-- **Window automation** (CLI → bridge → Win32): launch editors (`kicad_open_board`, `kicad_open_schematic`, …), list open editors, capture screenshots of all KiCad windows, send keystrokes, click at coords.-- **Reverse path** (CLI → bridge → in-process plugin inside KiCad): `kicad_bridge_call` invokes a Python handler running INSIDE the running KiCad GUI exe, with access to `pcbnew.*` / future `kipy`. This gives Docker-side Claude live introspection of the actually-open board without a round-trip through file exports.+### `open_board` -## `bridge.json`+Opens a PCB file in KiCad's PCB editor (pcbnew).  ```json {-  "manifest_version": 1,-  "name": "kicad",-  "displayName": "KiCad EDA",-  "version": "0.8.2",-  "description": "Reverse bridge for KiCad — board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.",-  "homepage": "https://github.com/adom-inc/adom-desktop",-  "author": "Adom Inc.",-  "license": "MIT",-  "spawn": {-    "kind": "python",-    "entrypoint": "server.py",-    "port": 8772,-    "healthEndpoint": "http://127.0.0.1:8772/status",-    "stopMethod": "kill",-    "killImageName": "python.exe"-  },-  "verbPrefixes": ["kicad_"],-  "verbs": [-    "kicad_open_board", "kicad_open_schematic", "kicad_open_symbol_editor",-    "kicad_open_footprint_editor", "kicad_open_3d_viewer",-    "kicad_list_versions",-    "kicad_run_drc", "kicad_run_erc",-    "kicad_lint_board", "kicad_lint_schematic", "kicad_lint_library",-    "kicad_format_upgrade",-    "kicad_install_plugin", "kicad_install_symbol", "kicad_install_footprint",-    "kicad_bridge_status", "kicad_bridge_call",-    "kicad_open_editors", "kicad_window_info", "kicad_screenshot_all",-    "kicad_send_key", "kicad_click", "kicad_close"-  ],-  "dependencies": { "python": ">=3.11", "kicad": ">=10.0" },-  "category": "EDA",-  "tags": ["pcb", "schematic", "open-source", "kicad-10", "kicad-11-ready"],-  "watcher": {-    "supports": true,-    "displayName": "Project Watch",-    "description": "Monitors a KiCad project folder for file changes and syncs them to Docker.",-    "defaultGlobs": ["*.kicad_pcb", "*.kicad_sch", "*.kicad_pro"],-    "configKey": "project_watch"+  "command": "open_board",+  "args": {+    "filePath": "C:/path/to/board.kicad_pcb"   } } ``` -The `watcher` field declares that this bridge supports project-watching — the GUI renders a 👁 pip on the chip when watching is active. See the [bridge SDK guide](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges#bridgejson-schema) for the full schema.+### `open_schematic` -## Architecture highlights+Opens a schematic file in KiCad's schematic editor (eeschema). +```json+{+  "command": "open_schematic",+  "args": {+    "filePath": "C:/path/to/schematic.kicad_sch"+  }+} ```-kicad/-├── server.py              ← HTTP entry point; routes /command to handlers/-├── handlers/              ← One module per logical verb group-│   ├── board_ops.py       ← kicad_run_drc, kicad_lint_board, kicad_open_board, …-│   ├── schematic_ops.py-│   ├── library_ops.py-│   ├── window_automation.py  ← Win32 SendInput, PrintWindow, EnumWindows-│   ├── bridge_call.py     ← Reverse bridge dispatch into the in-process plugin-│   └── …-├── kicad_detect.py        ← Find installed KiCad versions, exe paths, USER_SITE-├── adom_library.py        ← Symbol/footprint discovery in sym-lib-table.txt-├── lib_table.py           ← Add/remove rows in *-lib-table files-├── plugin_install.py      ← Copy adom_bridge.py into USER_SITE/usercustomize.py-├── plugin_payload/-│   └── adom_bridge.py     ← Reverse-bridge plugin loaded INSIDE every KiCad GUI exe-├── parsers/               ← .kicad_sym, .kicad_mod, .kicad_pcb tokenizers-└── start.bat              ← Windows launcher for local dev++### `run_drc`++Runs a Design Rule Check on a PCB file using `kicad-cli` and returns structured results.++```json+{+  "command": "run_drc",+  "args": {+    "filePath": "C:/path/to/board.kicad_pcb"+  }+} ``` -The reverse-bridge plugin (`plugin_payload/adom_bridge.py`) is what makes the kicad bridge special. KiCad's bundled Python interpreter overrides `site.USER_SITE` to `%USERPROFILE%\Documents\KiCad\10.0\3rdparty\Python311\site-packages\`, and the `usercustomize.py` import hook fires on every KiCad GUI exe start (`kicad.exe`, `pcbnew.exe`, `eeschema.exe`, `kicad-cli.exe`). Our plugin uses this hook to launch a tiny websocket client that connects to the bridge — turning every running KiCad into a remote-controllable instance.+Response includes parsed violations: -## Reverse-bridge example+```json+{+  "success": true,+  "output": "DRC complete: 2 error(s), 1 warning(s), 3 total",+  "data": {+    "violations": [+      {+        "type": "clearance",+        "severity": "error",+        "description": "Clearance violation (0.15mm < 0.2mm)",+        "items": [{"description": "U1 pad 3", "pos": {"x": 100, "y": 50}}]+      }+    ],+    "summary": {"total": 3, "errors": 2, "warnings": 1}+  }+}+``` -```bash-# From cloud Docker: list every open KiCad GUI exe + which board it has loaded-adom-desktop kicad_open_editors+### `install_symbol` (preferred for symbols) -# Direct in-process call: ask the running pcbnew.exe what board it has open-adom-desktop kicad_bridge_call '{"method":"get_board_info"}'+Decodes a base64-encoded `.kicad_sym` file and merges the symbol(s) into the single global **Adom** library (`Adom.kicad_sym`). All Adom-installed symbols live in one KiCad library. The library is created automatically on first use and registered in `sym-lib-table`. Subsequent installs add or update symbols in the same file. -# Run a DRC IN-PROCESS — no file write, no kicad-cli detour-adom-desktop kicad_bridge_call '{"method":"run_drc","args":{"severity":"error"}}'+```json+{+  "command": "install_symbol",+  "args": {+    "fileName": "BQ76952.kicad_sym",+    "fileContent": "<base64-encoded-.kicad_sym-file>",+    "symbolName": "BQ76952",+    "openEditor": true+  }+} ``` -The bridge dispatches these to `adom_bridge.py` running inside pcbnew. The handler executes against the live `pcbnew.GetBoard()` and returns JSON over the websocket.+| Arg | Required | Description |+|-----|----------|-------------|+| `fileName` | yes | Original filename (`.kicad_sym` extension added if missing) |+| `fileContent` | yes | Base64-encoded `.kicad_sym` file content |+| `symbolName` | no | Specific symbol name to navigate to in the editor |+| `openEditor` | no | Whether to open Symbol Editor after install (default: `true`) | -## Reference: install the source locally to read+**What it does internally:**+1. Decodes base64 `fileContent`+2. Extracts `(symbol ...)` blocks from the file+3. Merges into `Adom.kicad_sym` (creates the library + registers in `sym-lib-table` if needed)+4. If the symbol already exists in the Adom library, it is updated in place+5. If `openEditor` is true: opens Symbol Editor and navigates to the symbol -```bash-# Pull the zip (no auth needed — wiki static URL)-curl -L https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/kicad-bridge-v0.8.2.zip -o /tmp/kicad-bridge.zip+**No KiCad restart needed** -- the Adom library is pre-registered at bridge startup, so symbol updates are available immediately. -# Extract-mkdir -p /tmp/kicad-bridge && cd /tmp/kicad-bridge && python3 -c "import zipfile; zipfile.ZipFile('/tmp/kicad-bridge.zip').extractall()"-ls -la+### `adom_library_status`++Query the current state of the Adom symbol library.++```json+{+  "command": "adom_library_status",+  "args": {}+} ``` -You'll see `server.py`, `bridge.json`, `handlers/`, `plugin_payload/`, etc. — fork the layout for your own bridge.+Returns `{exists, registered, created, symbols}`. If the library is missing (e.g. user reset global libraries), calling this command will recreate it.++### `fix_keyboard`++Recovery command for when KiCad UI automation leaves modifier keys (Alt, Ctrl, Shift, Win) stuck.++```json+{+  "command": "fix_keyboard",+  "args": {}+}+```++Sends key-up events for all modifier keys and unlocks the foreground window lock. Safe to call even if nothing is stuck.++## Recommended Workflows++### Install and view a symbol (preferred -- single command)++```+1. install_symbol  (fileName, fileContent, symbolName, openEditor: true)+   -- Merges symbol into the global "Adom" library, opens Symbol Editor+   -- No KiCad restart needed (library is pre-registered at bridge startup)+```++### Install and view a symbol (multi-step alternative)++```+1. install_library     (install the .kicad_sym file as a separate library)+2. close_kicad         (ensure KiCad reloads library tables on next launch)+3. open_symbol_editor  (launches KiCad, opens Symbol Editor, loads the symbol)+Note: Creates a separate library per symbol. Prefer install_symbol which merges into the single Adom library.+```++### Install a footprint with 3D model and view it++```+1. install_library        (install .kicad_mod with model3dPath for .wrl/.step file)+2. close_kicad            (ensure KiCad reloads library tables on next launch)+3. open_footprint_editor  (launches KiCad, opens Footprint Editor, loads the footprint)+4. open_3d_viewer         (shows the 3D model of the loaded footprint)+```++### Iterate on a symbol design++```+1. install_symbol      (updated fileContent, same symbolName)+   -- Updates the symbol in the Adom library and reopens editor+   -- No restart needed since the library is already registered+```++### Iterate on a footprint design++```+1. install_library         (install/update the .kicad_mod file, optionally with 3D model)+2. close_kicad             (close everything to reload libraries)+3. open_footprint_editor   (open the updated footprint for review)+4. open_3d_viewer          (optionally check the 3D model)+5. close_3d_viewer         (close the viewer)+6. close_footprint_editor  (clean up when done)+```++### Full component review (symbol + footprint + 3D)++```+1. install_symbol          (symbol via single Adom library)+2. install_library         (footprint .kicad_mod with model3dPath)+3. close_kicad             (reload footprint library tables)+4. open_symbol_editor      (review symbol -- already in Adom library)+5. close_symbol_editor+6. open_footprint_editor   (review footprint)+7. open_3d_viewer          (review 3D model)+8. close_3d_viewer+9. close_footprint_editor+```++## File Structure++```+plugins/kicad/+├── server.py                  # HTTP server (entry point), ensures Adom library on startup+├── kicad_detect.py            # Auto-detects KiCad installation paths+├── lib_table.py               # S-expression parser for sym-lib-table / fp-lib-table+├── adom_library.py            # Single global Adom symbol library management+├── navigate_symbol.ps1        # PowerShell helper for editor UI automation+├── launch_editor.ps1          # PowerShell helper for launching KiCad editor windows+├── handlers/+│   ├── __init__.py+│   ├── open_files.py          # open_board, open_schematic+│   ├── open_symbol_editor.py  # Win32 automation to open Symbol Editor + load symbol+│   ├── open_footprint_editor.py # Win32 automation to open Footprint Editor + 3D Viewer+│   ├── close_windows.py       # close_symbol_editor, close_footprint_editor, close_3d_viewer, close_kicad+│   ├── install_library.py     # Library installation + table modification (symbols + footprints + 3D models)+│   ├── install_symbol.py      # Decode + merge symbol into Adom library + open editor+│   ├── run_drc.py             # DRC via kicad-cli with JSON parsing+│   └── fix_keyboard.py        # Release stuck modifier keys + unlock foreground+├── start.bat                  # Windows launcher script+└── README.md                  # This file+```++## Integration with Adom Desktop++The Tauri app's `commands.rs` forwards all KiCad commands (`"app": "kicad"`) to this bridge server via HTTP POST. The bridge server is **auto-started** when Adom Desktop launches and **auto-restarted** if it's not running when a command arrives. On startup, the bridge calls `ensure_adom_library()` to guarantee the global Adom symbol library exists and is registered in KiCad's `sym-lib-table`. -## Related+## Configuration -- **[Bridge SDK guide](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-bridges)** — `bridge.json` schema, packaging recipe, lifecycle-- **[Puppeteer bridge reference](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-puppeteer-bridge)** — simpler architecture, single-instance, Node + Chrome for Testing-- **[Fusion 360 bridge reference](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop-fusion-bridge)** — passthrough-to-add-in pattern-- **[adom-desktop main page](https://wiki-ufypy5dpx93o.adom.cloud/wiki/apps/adom-desktop)** — install the parent app first\ No newline at end of file+- **Port**: Default `8772`, override with `--port <N>`+- **KiCad detection**: Searches `C:/Program Files/KiCad/{9.0,8.0,7.0}/` automatically+- **Config dir**: `%APPDATA%/kicad/<version>/` (library tables live here)+- **User dir**: `~/Documents/KiCad/<version>/` (installed libraries are copied here)
SKILL.md+107−5
@@ -1,7 +1,109 @@ ----name: adom-desktop-kicad-bridge-description: >-  Reference implementation of the KiCad bridge — multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.+name: adom-desktop-kicad+public: true+description: "Launch and drive KiCad on the user's laptop (KiCad 7/8/9/10 supported, multi-version side-by-side) from this container via the adom-desktop CLI. Open schematics/boards/symbols/footprints, run DRC, install sym-lib-table / fp-lib-table libraries, show the 3D viewer, capture KiCad window screenshots, send keyboard shortcuts and click coordinates. Trigger words: kicad, open schematic, open board, open pcb, open symbol, open footprint, KiCad symbol editor, KiCad footprint editor, KiCad 3D viewer, run DRC, install KiCad library, sym-lib-table, fp-lib-table, KiCad screenshot, kicad_send_key, kicad_click, kicad_window_info, kicad_screenshot_all, close KiCad, kicad versions, kicad_list_versions, KiCad bridge." ----# Reference bridge: KiCad-Reference implementation of the KiCad bridge — multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.++# adom-desktop — KiCad bridge++All commands dispatch to the KiCad Python bridge running alongside the desktop app. Invoke from this container via Bash:++```bash+adom-desktop kicad_<action> '<json_args>'+```++The leading `kicad_` routes the call to the KiCad plugin; the `<action>` names below are the plugin-side command names.++## Multi-version (KiCad 9 + KiCad 10 side-by-side)++Users can have multiple KiCad versions installed at the same time. The bridge auto-discovers everything under `C:/Program Files/KiCad/<version>/` and **defaults to the newest installed version** if you don't specify one.++```bash+# Discover what's installed (run this first if unsure)+adom-desktop kicad_list_versions '{}'+# → {"versions":[{"version":"10.0", "default":true, ...}, {"version":"9.0", ...}], "default":"10.0", "count":2}+```++Every `kicad_open_*` (and `install_*`, `run_drc`) command accepts an optional **`kicadVersion`** arg to pin a specific install:++```bash+# Open in KiCad 10 (newest — default if you omit kicadVersion)+adom-desktop kicad_open_board '{"filePath":"C:/designs/foo.kicad_pcb"}'++# Open in KiCad 9 explicitly (e.g. to check that an older project still works)+adom-desktop kicad_open_board '{"filePath":"C:/designs/foo.kicad_pcb","kicadVersion":"9.0"}'+```++If you pass a version that's not installed, the bridge returns a hint listing the installed versions. The success response includes `kicadVersionUsed` so you can confirm which install actually ran.++> **Note on libraries / config**: each KiCad version has its own `sym-lib-table` and `fp-lib-table` (in `%APPDATA%/kicad/<version>/`). `kicad_install_library` writes to the table of whichever version was selected — so if the user installs a library and then asks "open it in KiCad 9", you may need to install it under both versions.++## Commands++| CLI form | Action | Purpose | Key args |+|---|---|---|---|+| `kicad_list_versions` | `list_versions` | Enumerate every installed KiCad version with paths and which one is the default | — |+| `kicad_open_schematic` | `open_schematic` | Open a `.kicad_sch` in the Schematic Editor | `filePath`, optional `kicadVersion` |+| `kicad_open_board` | `open_board` | Open a `.kicad_pcb` in the PCB Editor | `filePath`, optional `kicadVersion` |+| `kicad_open_symbol_editor` | `open_symbol_editor` | Launch Symbol Editor (optional: open specific library/symbol) | `libraryName`, `symbolName`, optional `kicadVersion` |+| `kicad_open_footprint_editor` | `open_footprint_editor` | Launch Footprint Editor | `libraryName`, `footprintName`, optional `kicadVersion` |+| `kicad_open_3d_viewer` | `open_3d_viewer` | Show the board (or footprint) in 3D viewer | `editor` ("auto"/"pcb"/"fp" — "auto" prefers the board when both editors are open, no need to close sub-editors), optional `kicadVersion` |+| `kicad_close_symbol_editor` | `close_symbol_editor` | Close Symbol Editor | — |+| `kicad_close_footprint_editor` | `close_footprint_editor` | Close Footprint Editor | — |+| `kicad_close_3d_viewer` | `close_3d_viewer` | Close 3D viewer | — |+| `kicad_close` | `close` | Close KiCad (all editors) | — |+| `kicad_window_info` | `window_info` | Enumerate open KiCad windows (HWND, title, bounds, editor type) | — |+| `kicad_install_library` | `install_library` | Register a `.kicad_sym` + `.pretty/` pair in sym-lib-table + fp-lib-table | `libraryPath`, `libraryType`, `libraryName`, optional `kicadVersion` |+| `kicad_install_symbol` | `install_symbol` | Add a single symbol to an existing library | `fileName`, `fileContent`, optional `kicadVersion` |+| `kicad_install_footprint` | `install_footprint` | Add a single footprint to an existing library | `library`, `footprint_path`, optional `kicadVersion` |+| `kicad_run_drc` | `run_drc` | Run DRC on the current board, return violations | `filePath`, optional `kicadVersion` |+| `kicad_fix_keyboard` | `fix_keyboard` | Workaround stuck-modifier bug after screen lock | — |+| `kicad_screenshot_all` | `screenshot_all` | Capture every open KiCad window (lossless PNGs) | — |+| `kicad_send_key` | `send_key` | Send a keystroke to a KiCad window — supports modifier combos ("alt+3", "ctrl+s", "ctrl+shift+s") | `hwnd`, `key` |+| `kicad_click` | `click` | Send a click at (x, y) in a KiCad window | `hwnd`, `x`, `y` |+| `kicad_adom_library_status` | `adom_library_status` | Report whether the Adom shared library is registered | — |++## Quick examples++```bash+# Open a schematic+adom-desktop kicad_open_schematic '{"path":"C:/designs/foo.kicad_sch"}'++# List open KiCad windows+adom-desktop kicad_window_info '{}'++# Screenshot every KiCad window+adom-desktop kicad_screenshot_all '{}'++# Run DRC+adom-desktop kicad_run_drc '{"board_path":"C:/designs/foo.kicad_pcb"}'++# Install a library pair+adom-desktop kicad_install_library '{+  "name":"my-lib",+  "sym_path":"C:/libs/my-lib.kicad_sym",+  "fp_path":"C:/libs/my-lib.pretty"+}'+```++## Error shape++Every failing response includes a `_hint` field with a human-readable next step — surface it to the user verbatim when a KiCad command fails.++## Bridge architecture (v1.8.31+) — you don't need to know any port++Earlier versions of the KiCad bridge listened on hardcoded port `8772`. **As of v1.8.31, bridges use OS-assigned ephemeral ports** — every spawn gets a new free port. You never need to know it.++- The CLI (`adom-desktop kicad_*`) automatically routes through adom-desktop's direct API (default port 47200 as of v1.8.33, with auto-fallback to 47201-47209 if 47200 is taken). adom-desktop then forwards to the bridge's actual runtime port.+- The bridge port is internal plumbing and can change every spawn. `bridge_list` reports it as `spawn.runtimePort` for debugging only — do NOT hardcode that anywhere.+- Why: this lets adom-desktop coexist with Hydrogen Desktop (which used to claim 8772 itself) and any other tool that grabs ports in the 8000 range. Side-by-side bridges from different apps no longer collide.+- The relay path (Docker → wss proxy → Windows GUI) is unchanged — same `adom-desktop kicad_*` verbs, same JSON shape.++If you ever find yourself wanting to talk directly to the KiCad bridge process: don't. Go through the verb dispatch. If the verb you need doesn't exist, request a feature in `adom-inc/adom-desktop`.++## Related++- `adom-desktop-discovery` (gallia) — ensures the CLI + relay are installed.+- `adom-desktop-fusion` — sibling bridge for Fusion 360.+- `adom-desktop-direct-api` — the direct API contract (port 47200 + 47201-47209 fallback, discovery file at `~/.adom/direct-api-port`).+- Repo: `adom-inc/adom-desktop/plugins/kicad/`
adom_library.pyadded+224
@@ -0,0 +1,224 @@+"""Manage the single global Adom symbol library (Adom.kicad_sym).++All symbols installed by Adom Desktop live in one KiCad symbol+library file.  This module handles:++- Creating the library file if it doesn't exist+- Registering it in sym-lib-table if not already there+- Adding/updating individual symbols inside the library+- Listing symbols currently in the library+"""++import re+from pathlib import Path++from lib_table import LibTable, LibEntry++# The one library name used everywhere+ADOM_LIB_NAME = "Adom"+ADOM_LIB_FILE = "Adom.kicad_sym"+ADOM_LIB_DESCRIPTION = "Adom Desktop - custom symbols"++# Regex to extract top-level (symbol "NAME" ...) blocks.+# Top-level symbols are indented with exactly one leading whitespace group+# (2 spaces typically) whereas sub-unit symbols have deeper indentation.+# We use a balanced-paren counter to find the closing paren.+_SYMBOL_NAME_RE = re.compile(r'^\s+\(symbol\s+"([^"]+)"', re.MULTILINE)++# Template for an empty Adom.kicad_sym+_EMPTY_LIB = (+    "(kicad_symbol_lib\n"+    '  (version 20231120)\n'+    '  (generator "adom-desktop")\n'+    ")\n"+)+++def _get_paths(kicad_info: dict) -> tuple[Path, Path] | None:+    """Return (sym_lib_table_path, adom_kicad_sym_path) or None."""+    config_dir = kicad_info.get("config_dir")+    user_dir = kicad_info.get("user_dir")+    if not config_dir or not user_dir:+        return None+    table_path = Path(config_dir) / "sym-lib-table"+    sym_dir = Path(user_dir) / "symbols"+    sym_dir.mkdir(parents=True, exist_ok=True)+    lib_path = sym_dir / ADOM_LIB_FILE+    return table_path, lib_path+++def ensure_adom_library(kicad_info: dict) -> dict:+    """Make sure Adom.kicad_sym exists and is registered.++    Returns:+        {+            "exists": bool,        # whether the file existed before this call+            "registered": bool,    # whether it was already in sym-lib-table+            "created": bool,       # whether we had to create the file+            "symbols": [str],      # symbol names currently in the library+        }+    """+    if not kicad_info.get("installed"):+        return {"exists": False, "registered": False, "created": False, "symbols": [],+                "error": "KiCad not installed"}++    paths = _get_paths(kicad_info)+    if not paths:+        return {"exists": False, "registered": False, "created": False, "symbols": [],+                "error": "KiCad config/user directory not found"}++    table_path, lib_path = paths++    # --- Ensure the .kicad_sym file exists ---+    existed = lib_path.exists()+    if not existed:+        lib_path.write_text(_EMPTY_LIB, encoding="utf-8")++    # --- Ensure it's registered in sym-lib-table ---+    table = LibTable.parse_file(str(table_path))+    was_registered = table.has_library(ADOM_LIB_NAME)++    if not was_registered:+        uri = str(lib_path).replace("\\", "/")+        entry = LibEntry(+            name=ADOM_LIB_NAME,+            lib_type="KiCad",+            uri=uri,+            options="",+            descr=ADOM_LIB_DESCRIPTION,+        )+        table.add_library(entry)+        table.write_file(str(table_path))++    # --- List current symbols ---+    symbols = list_symbols(lib_path)++    return {+        "exists": existed,+        "registered": was_registered,+        "created": not existed,+        "symbols": symbols,+    }+++def list_symbols(lib_path: Path) -> list[str]:+    """Return the names of all top-level symbols in a .kicad_sym file."""+    if not lib_path.exists():+        return []+    text = lib_path.read_text(encoding="utf-8")+    # Top-level symbols: (symbol "NAME" at indentation level 1 (2 spaces)+    # Sub-units like "NAME_0_0" are children — we skip those.+    names = []+    for m in _SYMBOL_NAME_RE.finditer(text):+        name = m.group(1)+        # Skip sub-unit symbols (contain _N_N suffix pattern)+        if not re.search(r"_\d+_\d+$", name):+            names.append(name)+    return names+++def _extract_symbol_block(text: str, start_pos: int) -> str:+    """Extract a complete (symbol ...) block starting from start_pos.++    Uses balanced parenthesis counting to find the matching close paren.+    """+    depth = 0+    i = start_pos+    while i < len(text):+        if text[i] == "(":+            depth += 1+        elif text[i] == ")":+            depth -= 1+            if depth == 0:+                return text[start_pos : i + 1]+        i += 1+    # Shouldn't happen with valid files, but return what we have+    return text[start_pos:]+++def _extract_top_level_symbols(text: str) -> list[tuple[str, str]]:+    """Extract all top-level (symbol "NAME" ...) blocks from a .kicad_sym.++    Returns list of (name, full_block_text) tuples.+    Each block includes the symbol and all its sub-units.+    """+    results = []+    # Find all top-level symbol openings (indented with exactly 2 spaces)+    for m in re.finditer(r'^  \(symbol "([^"]+)"', text, re.MULTILINE):+        name = m.group(1)+        # Skip sub-unit symbols+        if re.search(r"_\d+_\d+$", name):+            continue+        block = _extract_symbol_block(text, m.start())+        results.append((name, block))+    return results+++def add_symbol(kicad_info: dict, symbol_text: str) -> dict:+    """Add or update a symbol in Adom.kicad_sym.++    Args:+        kicad_info: KiCad detection info dict.+        symbol_text: Full content of a .kicad_sym file containing the symbol(s).++    Returns:+        {+            "success": bool,+            "added": [str],     # names of symbols added/updated+            "lib_path": str,    # path to Adom.kicad_sym+        }+    """+    # Ensure library exists first+    ensure_result = ensure_adom_library(kicad_info)+    if ensure_result.get("error"):+        return {"success": False, "error": ensure_result["error"]}++    paths = _get_paths(kicad_info)+    if not paths:+        return {"success": False, "error": "KiCad paths not found"}++    _, lib_path = paths++    # Extract symbols from incoming text+    incoming_symbols = _extract_top_level_symbols(symbol_text)+    if not incoming_symbols:+        return {"success": False, "error": "No symbols found in the provided file content"}++    # Read the existing library+    existing_text = lib_path.read_text(encoding="utf-8")++    # Extract existing symbols+    existing_symbols = _extract_top_level_symbols(existing_text)+    existing_by_name = {name: block for name, block in existing_symbols}++    # Merge: update existing, add new+    added_names = []+    for name, block in incoming_symbols:+        existing_by_name[name] = block+        added_names.append(name)++    # Rebuild the library file+    # Parse header (version + generator lines)+    header_match = re.match(+        r'(\(kicad_symbol_lib\s*\n(?:\s+\(version[^\)]+\)\s*\n)?(?:\s+\(generator[^\)]+\)\s*\n)?)',+        existing_text,+    )+    if header_match:+        header = header_match.group(1)+    else:+        header = "(kicad_symbol_lib\n  (version 20231120)\n  (generator \"adom-desktop\")\n"++    # Build new file content+    parts = [header]+    for _name, block in existing_by_name.items():+        parts.append(f"  {block.strip()}\n")+    parts.append(")\n")++    new_text = "".join(parts)+    lib_path.write_text(new_text, encoding="utf-8")++    return {+        "success": True,+        "added": added_names,+        "lib_path": str(lib_path),+    }
bridge.jsonadded+83
@@ -0,0 +1,83 @@+{+  "manifest_version": 1,+  "name": "kicad",+  "displayName": "KiCad EDA",+  "version": "0.9.0",+  "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.",+  "homepage": "https://github.com/adom-inc/adom-desktop",+  "author": "Adom Inc.",+  "license": "MIT",+  "spawn": {+    "kind": "python",+    "entrypoint": "server.py",+    "port": 0,+    "healthEndpoint": "/status",+    "stopMethod": "kill",+    "killImageName": "python.exe"+  },+  "verbPrefixes": [+    "kicad_"+  ],+  "verbs": [+    "kicad_open_board",+    "kicad_open_schematic",+    "kicad_open_symbol_editor",+    "kicad_open_footprint_editor",+    "kicad_open_3d_viewer",+    "kicad_list_versions",+    "kicad_run_drc",+    "kicad_run_erc",+    "kicad_lint_board",+    "kicad_lint_schematic",+    "kicad_lint_library",+    "kicad_format_upgrade",+    "kicad_install_plugin",+    "kicad_install_symbol",+    "kicad_install_footprint",+    "kicad_bridge_status",+    "kicad_bridge_call",+    "kicad_open_editors",+    "kicad_window_info",+    "kicad_screenshot_all",+    "kicad_send_key",+    "kicad_click",+    "kicad_close"+  ],+  "dependencies": {+    "python": ">=3.11",+    "kicad": ">=10.0"+  },+  "category": "EDA",+  "tags": [+    "pcb",+    "schematic",+    "open-source",+    "kicad-10",+    "kicad-11-ready"+  ],+  "docs": "https://wiki.adom.inc/adom/adom-desktop-kicad-bridge",+  "platforms": {+    "windows": {+      "supported": true+    },+    "macos": {+      "supported": false,+      "reason": "macOS port pending \u2014 kicad-cli verbs work, but the Win32 window-automation handlers (kicad_send_key, kicad_click, kicad_screenshot_all) need a Quartz/AppKit equivalent."+    },+    "linux": {+      "supported": false,+      "reason": "Linux port pending \u2014 kicad-cli works, but Win32 window automation doesn't translate to X11/Wayland."+    }+  },+  "watcher": {+    "supports": true,+    "displayName": "Project Watch",+    "description": "Monitors a KiCad project folder for file changes and syncs them to Docker. Configure path + cadence in Settings \u2192 KiCad.",+    "defaultGlobs": [+      "*.kicad_pcb",+      "*.kicad_sch",+      "*.kicad_pro"+    ],+    "configKey": "project_watch"+  }+}\ No newline at end of file
capabilities.pyadded+196
@@ -0,0 +1,196 @@+"""KiCad automation-capability reporting (version-awareness).++The bridge should be able to tell the user, in plain terms:+  - what automation THEIR installed KiCad supports,+  - what's gated behind enabling the API server,+  - and when upgrading KiCad would unlock more deterministic automation.++This is deliberately separate from kicad_detect.py: detection answers+"what's installed and where"; this answers "what can we actually drive,+and what should the user do to get more."++Ground truth (measured against KiCad 10.0.1 on 2026-06-25, and the+public KiCad IPC-API roadmap):++  * KiCad < 9.0  → no IPC API at all. Only kicad-cli (headless convert/+    lint/erc/drc) + GUI launch. Symbol/footprint *delivery* works (file ++    lib-table), but nothing can be driven on the canvas.+  * KiCad 9.0 / 10.x → IPC API present but **PCB-editor-focused**. The+    board can be driven deterministically (open pcbnew → get_board() →+    place). The schematic/symbol editor exposes **no** document/board+    handler yet (GetOpenDocuments → "no handler available"), so opening a+    *symbol* on the canvas is NOT deterministic — it falls back to+    assisted automation.+  * KiCad 11 (when it ships) → SWIG bindings removed; IPC is the only+    Python path and is expected to broaden to the schematic/symbol editor.+    When it does, symbol delivery flips from "assisted" to "ipc" with no+    caller change — they just read `automation.symbol`.++The IPC API also requires the user to ENABLE the API server+(Preferences > Plugins > "Enable KiCad API server"); we detect that from+kicad_common.json so we can tell them (or fix it) instead of timing out.+"""++import json+import re+from pathlib import Path++# Latest stable KiCad major we know about, for "is an upgrade available?"+# messaging. Bump when a new stable major ships. (KiCad 10 is current as of+# 2026-06; 11 removes SWIG and is expected to broaden IPC.)+LATEST_KNOWN_STABLE_MAJOR = 10+++def _version_major_minor(version: str) -> tuple[int, int]:+    m = re.match(r"^(\d+)(?:\.(\d+))?", str(version or ""))+    if not m:+        return (0, 0)+    return (int(m.group(1)), int(m.group(2) or 0))+++def _api_server_enabled(config_dir: str | None) -> bool | None:+    """Read api.enable_server from <config_dir>/kicad_common.json.++    Returns True/False if we can read it, or None if the file/field is+    missing (older KiCad without the setting, or unreadable). The bridge+    needs this on for any IPC call to connect — when it's off, every IPC+    verb otherwise times out silently.+    """+    if not config_dir:+        return None+    common = Path(config_dir) / "kicad_common.json"+    if not common.exists():+        return None+    try:+        data = json.loads(common.read_text(encoding="utf-8", errors="replace"))+    except (OSError, ValueError):+        return None+    api = data.get("api")+    if not isinstance(api, dict) or "enable_server" not in api:+        return None+    return bool(api.get("enable_server"))+++def capability_for_version(info: dict) -> dict:+    """Build the capability report for a single installed KiCad version."""+    version = info.get("version") or "0.0"+    major, _minor = _version_major_minor(version)++    ipc_supported = major >= 9          # KiCad 9.0+ ships the IPC API server+    api_enabled = _api_server_enabled(info.get("config_dir"))++    # What can each artifact kind be driven by, on THIS version?+    #   "ipc"      → deterministic via the IPC API (best path; no AI)+    #   "assisted" → IPC can't do it yet; needs assisted/AI automation+    #   "cli"      → only file delivery + headless kicad-cli; no canvas drive+    if ipc_supported:+        automation = {+            "footprint": "ipc",        # open pcbnew → get_board() → place+            "board": "ipc",+            "symbol": "assisted",      # no schematic/symbol IPC handler yet+            "schematic": "assisted",+        }+    else:+        automation = {+            "footprint": "cli",+            "board": "cli",+            "symbol": "cli",+            "schematic": "cli",+        }++    # Upgrade guidance — the "hey, your KiCad doesn't have the automation+    # APIs, let's upgrade you" message the user asked for.+    upgrade = None+    if not ipc_supported:+        upgrade = {+            "recommended": True,+            "reason": (+                f"KiCad {version} predates the IPC automation API (added in 9.0). "+                f"Upgrading unlocks deterministic, no-AI footprint/board automation "+                f"and live canvas control."+            ),+            "unlocks": ["ipc_api", "deterministic_footprint", "deterministic_board"],+        }+    elif major < LATEST_KNOWN_STABLE_MAJOR:+        upgrade = {+            "recommended": False,+            "reason": (+                f"KiCad {version} has the IPC API but a newer stable "+                f"(KiCad {LATEST_KNOWN_STABLE_MAJOR}+) may broaden automation."+            ),+            "unlocks": [],+        }++    notes = []+    if ipc_supported and api_enabled is False:+        notes.append(+            "IPC API server is DISABLED (Preferences > Plugins > 'Enable KiCad "+            "API server'). Enable it (the bridge can do this for you) or every "+            "IPC automation call will hang."+        )+    if ipc_supported and automation["symbol"] == "assisted":+        notes.append(+            "Symbol-editor IPC is not exposed in this KiCad yet, so opening a "+            "symbol on the canvas uses assisted automation. It will become "+            "deterministic automatically when KiCad ships the schematic IPC API."+        )++    return {+        "version": version,+        "ipcApiSupported": ipc_supported,      # does this KiCad have the API at all+        "ipcApiServerEnabled": api_enabled,    # True/False/None (None = unknown/older)+        "ipcClientInstalled": bool(info.get("ipcApiAvailable")),  # kipy importable+        "automation": automation,+        "upgrade": upgrade,+        "notes": notes,+    }+++def build_capability_report(all_kicad_versions: list[dict]) -> dict:+    """Top-level capability map across all installed KiCad versions.++    Shape:+        {+          "kicadInstalled": bool,+          "latestInstalled": "10.0" | None,+          "perVersion": [ capability_for_version(...) , ... ],+          "summary": "<one-line human summary for the newest install>",+        }+    """+    if not all_kicad_versions:+        return {+            "kicadInstalled": False,+            "latestInstalled": None,+            "perVersion": [],+            "summary": (+                "KiCad is not installed. The bridge can install it for you "+                "(winget) — ask to 'install KiCad'."+            ),+        }++    per_version = [capability_for_version(v) for v in all_kicad_versions]+    newest = per_version[0]+    nv = newest["version"]++    if not newest["ipcApiSupported"]:+        summary = (+            f"KiCad {nv} is installed but too old for the automation API. "+            f"Upgrade to KiCad {LATEST_KNOWN_STABLE_MAJOR}+ to unlock deterministic automation."+        )+    elif newest["ipcApiServerEnabled"] is False:+        summary = (+            f"KiCad {nv} supports IPC automation, but the API server is OFF. "+            f"Enable it to drive KiCad (the bridge can do this for you)."+        )+    else:+        summary = (+            f"KiCad {nv}: IPC automation ready. Footprints/boards are deterministic; "+            f"symbols use assisted automation until KiCad exposes the schematic IPC API."+        )++    return {+        "kicadInstalled": True,+        "latestInstalled": nv,+        "perVersion": per_version,+        "summary": summary,+    }
kicad_detect.pyadded+856
@@ -0,0 +1,856 @@+"""Auto-detect KiCad installation on Windows and macOS.++Windows: globs C:/Program Files/KiCad/<version>/ for parallel installs.+macOS:   scans /Applications/ for KiCad.app bundles (KiCad.app, KiCad-nightly.app, etc.)+         and reads CFBundleShortVersionString from Info.plist for the version.++Both platforms return the same dict shape so handlers can stay platform-agnostic.+Returns a sorted list (newest first); callers default to the latest unless they+explicitly request an older version via the `kicadVersion` arg on commands.+"""++import os+import re+import subprocess+import sys+from pathlib import Path++IS_WINDOWS = sys.platform == "win32"+IS_MACOS = sys.platform == "darwin"++# ── Windows paths ────────────────────────────────────────────────────────────+# v1.6.0+: scan ALL the locations a Windows KiCad install might land. v1.5.x+# only checked C:/Program Files/KiCad — that missed at least one user whose+# install was elsewhere (winget per-user install, custom dir, etc.) on a+# fresh-Windows-machine deploy. We now also check %LOCALAPPDATA%/Programs/+# (winget --scope user target) and the Windows registry's KiCad install key.+WIN_KICAD_BASES = [+    Path("C:/Program Files/KiCad"),+    Path("C:/Program Files (x86)/KiCad"),+]+_local = os.environ.get("LOCALAPPDATA")+if _local:+    WIN_KICAD_BASES.append(Path(_local) / "Programs" / "KiCad")+# Also look directly in LOCALAPPDATA (rare, but seen in some manual installs)+if _local:+    WIN_KICAD_BASES.append(Path(_local) / "KiCad")+# Legacy single-base name kept for back-compat with any callers that import it+WIN_KICAD_BASE = WIN_KICAD_BASES[0]++# ── macOS paths ──────────────────────────────────────────────────────────────+# KiCad on macOS installs individual .app bundles into /Applications/KiCad/.+# The "main" bundle is KiCad.app; Schematic Editor.app, PCB Editor.app, etc.+# are siblings and share the same install version.+MAC_APPS_DIR = Path("/Applications")+MAC_KICAD_DIR = MAC_APPS_DIR / "KiCad"+++def _version_tuple(version: str):+    """Parse '10.0' / '9.0' / '8.0.4' into a sortable tuple. Falls back to (0,) on parse error."""+    parts = []+    for piece in re.split(r"[.\-_]", str(version)):+        m = re.match(r"^(\d+)", piece)+        if m:+            parts.append(int(m.group(1)))+        else:+            parts.append(0)+    return tuple(parts) if parts else (0,)+++# ╔═══════════════════════════════════════════════════════════════════════════╗+# ║                              WINDOWS                                       ║+# ╚═══════════════════════════════════════════════════════════════════════════╝++def _win_build_info(version: str, base_override: Path | None = None) -> dict | None:+    """Build the kicad_info dict for a Windows install version dir, or None+    if it doesn't look like a real install. v1.6.0+: accepts an explicit+    base override so we can describe installs in non-standard locations+    (LOCALAPPDATA/Programs, custom dirs from registry) the same way the+    Program Files install gets described.++    v1.7.4+ (KiCad v2 Phase 1): enriched with pythonVersion + pluginDir ++    adomPlugin status + IPC API probe + libraryTablePaths grouping. The+    raw fields stay (back-compat); the new fields are additive.+    """+    base = (base_override or WIN_KICAD_BASE) / version if base_override is None or not base_override.name == version else base_override+    # If caller passed a base that already names the version dir, base==base_override.+    # Otherwise we joined version onto a parent. Either way `base` is the version dir.+    bin_dir = base / "bin"+    kicad_exe = bin_dir / "kicad.exe"++    if not kicad_exe.exists():+        return None++    config_dir = _win_find_config_dir(version)+    user_dir = _win_find_user_dir(version)+    # v1.7.4+: also resolve the per-version plugin dir + Adom plugin status.+    plugin_dir = _win_find_plugin_dir(version)+    adom_plugin_state = _adom_plugin_state(plugin_dir)+    # v1.7.5+ (KiCad v2 Phase 2): the cross-process reverse bridge lives+    # at the KiCad embedded-Python USER_SITE, not the old pcbnew action-+    # plugin dir. usercustomize.py + adom_bridge.py land here and load+    # in every KiCad GUI process at startup.+    user_site_dir = _win_find_user_site_dir(version)+    bridge_plugin_state = _adom_bridge_state(user_site_dir)+    # Probe the bundled python for version + IPC API availability. Cheap+    # subprocess call — only runs once per detection pass.+    python_exe = bin_dir / "python.exe"+    py_version = _probe_python_version(python_exe)+    ipc_state = _probe_ipc_api(python_exe)+    # Adom library inventory — read the user's sym-lib-table + fp-lib-table+    # for entries whose name starts with "Adom" and count symbols/footprints.+    adom_lib_state = _adom_library_stats(+        config_dir / "sym-lib-table" if config_dir else None,+        config_dir / "fp-lib-table" if config_dir else None,+        version,+    )++    return {+        # ── Legacy fields (kept verbatim for back-compat with v1.7.3 callers) ──+        "installed": True,+        "platform": "windows",+        "version": version,+        "base_dir": str(base),+        "bin_dir": str(bin_dir),+        "kicad_exe": str(kicad_exe),+        "pcbnew_exe": str(bin_dir / "pcbnew.exe"),+        "eeschema_exe": str(bin_dir / "eeschema.exe"),+        "kicad_cli_exe": str(bin_dir / "kicad-cli.exe"),+        "python_exe": str(python_exe),+        "config_dir": str(config_dir) if config_dir else None,+        "user_dir": str(user_dir) if user_dir else None,+        "sym_lib_table": str(config_dir / "sym-lib-table") if config_dir else None,+        "fp_lib_table": str(config_dir / "fp-lib-table") if config_dir else None,+        "user_symbols_dir": str(user_dir / "symbols") if user_dir else None,+        "user_footprints_dir": str(user_dir / "footprints") if user_dir else None,+        "user_3dmodels_dir": str(user_dir / "3dmodels") if user_dir else None,+        # ── v1.7.4 enriched fields (KiCad v2 Phase 1) ──+        "installPath": str(base),                       # alias for base_dir, camelCase per PLAN+        "binaries": {                                   # grouped per PLAN.md+            "kicad": str(kicad_exe),+            "eeschema": str(bin_dir / "eeschema.exe"),+            "pcbnew": str(bin_dir / "pcbnew.exe"),+            "kicadCli": str(bin_dir / "kicad-cli.exe"),+            "python": str(python_exe),+        },+        "pythonVersion": py_version,                    # e.g. "3.11.5", or None if probe failed+        "pluginDir": str(plugin_dir) if plugin_dir else None,+        "adomPluginInstalled": adom_plugin_state["installed"],+        "adomPluginVersion": adom_plugin_state["version"],+        # v1.7.5+ KiCad v2 Phase 2: cross-process reverse bridge state+        "userSiteDir": str(user_site_dir) if user_site_dir else None,+        "adomBridgeInstalled": bridge_plugin_state["installed"],+        "adomBridgeVersion": bridge_plugin_state["version"],+        "adomBridgeLoaderInstalled": bridge_plugin_state["loaderInstalled"],+        "ipcApiAvailable": ipc_state["available"],      # False on KiCad ≤10; True if `kipy` importable+        "ipcApiScope": ipc_state["scope"],              # ["pcb"] etc.; [] when not available+        "libraryTablePaths": {                          # grouped per PLAN.md+            "symLibTable": str(config_dir / "sym-lib-table") if config_dir else None,+            "fpLibTable": str(config_dir / "fp-lib-table") if config_dir else None,+        },+        "adomLibrary": adom_lib_state,                  # symbol/footprint inventory+    }+++def _win_find_config_dir(version: str):+    """Find KiCad config dir on Windows: %APPDATA%/kicad/<version>/"""+    appdata = os.environ.get("APPDATA", "")+    if not appdata:+        return None+    config = Path(appdata) / "kicad" / version+    return config if config.exists() else None+++def _win_find_user_dir(version: str):+    """Find KiCad user dir on Windows: %USERPROFILE%/Documents/KiCad/<version>/"""+    userprofile = os.environ.get("USERPROFILE", "")+    if not userprofile:+        return None+    user = Path(userprofile) / "Documents" / "KiCad" / version+    return user if user.exists() else None+++# v1.7.4+ (KiCad v2 Phase 1): enriched-discovery helpers.+# All return None / empty / False on failure — never raise. Detection has to be+# best-effort: the caller iterates over multiple installs and one buggy install+# shouldn't break the whole list.++def _win_find_user_site_dir(version: str) -> Path | None:+    """Find KiCad's embedded-Python USER_SITE on Windows.++    KiCad's bundled `sitecustomize.py` overrides `site.USER_SITE` to+    `%USERPROFILE%/Documents/KiCad/<version>/3rdparty/Python311/site-packages/`.+    This is where Phase 2's reverse-bridge plugin (adom_bridge.py ++    usercustomize.py) is deployed — usercustomize.py loads automatically+    for every KiCad GUI process at Python init.++    Returns the Path regardless of whether the dir exists. plugin_install+    handles mkdir at deploy time.+    """+    userprofile = os.environ.get("USERPROFILE", "")+    if not userprofile:+        return None+    return Path(userprofile) / "Documents" / "KiCad" / version / "3rdparty" / "Python311" / "site-packages"+++def _adom_bridge_state(user_site_dir: Path | None) -> dict:+    """Check whether the Phase 2 reverse-bridge plugin is installed at+    the given USER_SITE.++    Returns {"installed": bool, "version": str|None, "loaderInstalled": bool}.++    "installed" = adom_bridge.py exists.+    "version"   = __version__ parsed from the first ~50 lines (regex).+    "loaderInstalled" = usercustomize.py exists (the loader that imports+                       adom_bridge at site init). Both files must be+                       present for the bridge to actually start.+    """+    if user_site_dir is None:+        return {"installed": False, "version": None, "loaderInstalled": False}+    bridge_path = user_site_dir / "adom_bridge.py"+    loader_path = user_site_dir / "usercustomize.py"+    if not bridge_path.exists():+        return {+            "installed": False,+            "version": None,+            "loaderInstalled": loader_path.exists(),+        }+    try:+        with open(bridge_path, "r", encoding="utf-8", errors="replace") as f:+            head = "".join(f.readline() for _ in range(50))+        m = re.search(r"""__version__\s*=\s*["']([^"']+)["']""", head)+        return {+            "installed": True,+            "version": m.group(1) if m else None,+            "loaderInstalled": loader_path.exists(),+        }+    except OSError:+        return {"installed": True, "version": None, "loaderInstalled": loader_path.exists()}+++def _win_find_plugin_dir(version: str) -> Path | None:+    """Find the per-version KiCad scripting plugin dir:+    `%APPDATA%/KiCad/<version>/scripting/plugins/`.++    Returns the Path regardless of whether the dir exists yet — caller may want+    to install the Adom plugin INTO it, so a non-existent dir is still useful+    information (vs None which means we couldn't even resolve APPDATA).+    """+    appdata = os.environ.get("APPDATA", "")+    if not appdata:+        return None+    return Path(appdata) / "KiCad" / version / "scripting" / "plugins"+++def _adom_plugin_state(plugin_dir: Path | None) -> dict:+    """Check whether the Adom bridge plugin (adom_bridge.py) is installed+    in the given KiCad scripting plugins dir, and what version it reports.++    Returns: {"installed": bool, "version": str|None}.++    Version is parsed from a `__version__ = "x.y.z"` line at the top of the+    plugin file. Falls back to None if the file exists but has no __version__.+    """+    if plugin_dir is None:+        return {"installed": False, "version": None}+    plugin_path = plugin_dir / "adom_bridge.py"+    if not plugin_path.exists():+        return {"installed": False, "version": None}+    # Read the first ~50 lines looking for `__version__`. Don't load/eval the+    # plugin code; just regex-extract the literal.+    try:+        with open(plugin_path, "r", encoding="utf-8", errors="replace") as f:+            head = "".join(f.readline() for _ in range(50))+        m = re.search(r"""__version__\s*=\s*["']([^"']+)["']""", head)+        return {"installed": True, "version": m.group(1) if m else None}+    except OSError:+        return {"installed": True, "version": None}+++def _probe_python_version(python_exe: Path) -> str | None:+    """Run `<kicad>/bin/python.exe --version` and parse the version string.+    Returns "3.11.5" etc., or None if the probe fails (python.exe missing,+    spawn error, timeout). Subprocess call costs ~30-80 ms.+    """+    if not python_exe.exists():+        return None+    try:+        result = subprocess.run(+            [str(python_exe), "--version"],+            capture_output=True, text=True, timeout=5,+        )+        # Python writes its version to stdout (>=3.4) or stderr (<3.4).+        out = (result.stdout or "") + (result.stderr or "")+        m = re.search(r"Python\s+(\d+\.\d+(?:\.\d+)?)", out)+        return m.group(1) if m else None+    except (subprocess.TimeoutExpired, OSError):+        return None+++def _adom_library_stats(+    sym_lib_table: Path | None,+    fp_lib_table: Path | None,+    version: str,+) -> dict:+    """Read the user's sym-lib-table + fp-lib-table for entries whose name+    starts with "Adom" (case-insensitive — handles "Adom", "adom",+    "Adom_Symbols", "AdomCustom", etc.) and count the symbols/footprints+    inside each matching library.++    Returns:+        {+            "symbolsInstalled": bool,        # any Adom sym-lib entry exists+            "footprintsInstalled": bool,     # any Adom fp-lib entry exists+            "symbolCount": int,              # total count across all Adom symbol libs+            "footprintCount": int,           # total count across all Adom .pretty dirs+            "symbolLibraries": [{name, uri, count}],   # per-entry detail+            "footprintLibraries": [{name, uri, count}],+        }++    Best-effort. Returns the empty-shape on any IO / parse error so callers+    can rely on the keys always being present.+    """+    out = {+        "symbolsInstalled": False,+        "footprintsInstalled": False,+        "symbolCount": 0,+        "footprintCount": 0,+        "symbolLibraries": [],+        "footprintLibraries": [],+    }++    # ── Symbols ──+    if sym_lib_table is not None and sym_lib_table.exists():+        for entry in _parse_lib_table_entries(sym_lib_table):+            if not entry["name"].lower().startswith("adom"):+                continue+            resolved_uri = _resolve_kicad_var(entry["uri"], version)+            count = _count_symbols_in_kicad_sym(resolved_uri)+            out["symbolLibraries"].append({+                "name": entry["name"],+                "uri": str(resolved_uri),+                "count": count,+            })+            out["symbolCount"] += count+        out["symbolsInstalled"] = len(out["symbolLibraries"]) > 0++    # ── Footprints ──+    if fp_lib_table is not None and fp_lib_table.exists():+        for entry in _parse_lib_table_entries(fp_lib_table):+            if not entry["name"].lower().startswith("adom"):+                continue+            resolved_uri = _resolve_kicad_var(entry["uri"], version)+            count = _count_footprints_in_pretty_dir(resolved_uri)+            out["footprintLibraries"].append({+                "name": entry["name"],+                "uri": str(resolved_uri),+                "count": count,+            })+            out["footprintCount"] += count+        out["footprintsInstalled"] = len(out["footprintLibraries"]) > 0++    return out+++def _parse_lib_table_entries(table_path: Path) -> list[dict]:+    """Parse a KiCad sym-lib-table or fp-lib-table file.++    Both formats are s-expressions like:+        (sym_lib_table+          (version 7)+          (lib (name "Adom")(type "KiCad")(uri "...")(options "")(descr "..."))+        )++    Returns one dict per (lib ...) entry: {name, type, uri, options, descr}.+    Best-effort regex parse — these files are small (~dozens of entries max)+    and the format is rigid enough that a full s-expr parser is overkill.+    """+    try:+        with open(table_path, "r", encoding="utf-8", errors="replace") as f:+            content = f.read()+    except OSError:+        return []++    entries = []+    # Each (lib ...) entry is on its own conceptual unit; match against the+    # five quoted-value fields. The whitespace between fields varies but+    # they always appear in this order.+    pattern = re.compile(+        r"""\(lib\s++            \(name\s+"([^"]*)"\)\s*+            \(type\s+"([^"]*)"\)\s*+            \(uri\s+"([^"]*)"\)\s*+            \(options\s+"([^"]*)"\)\s*+            \(descr\s+"([^"]*)"\)\s*+            \)""",+        re.VERBOSE | re.DOTALL,+    )+    for m in pattern.finditer(content):+        entries.append({+            "name": m.group(1),+            "type": m.group(2),+            "uri": m.group(3),+            "options": m.group(4),+            "descr": m.group(5),+        })+    return entries+++def _resolve_kicad_var(uri: str, version: str) -> Path:+    """Resolve KiCad path variables in a library URI. KiCad uses+    `${VAR_NAME}` syntax for things like `${KICAD_USER_TEMPLATE_DIR}`,+    `${KIPRJMOD}`, `${KICAD9_FOOTPRINT_DIR}`, etc.++    For the Adom-library case, the URIs in the user's tables we've seen+    are absolute paths (no vars). But we handle the common ones for+    robustness.+    """+    userprofile = os.environ.get("USERPROFILE", "")+    documents = Path(userprofile) / "Documents" if userprofile else None+    # Common KiCad path vars + their typical resolved values+    substitutions = {+        "KICAD_USER_TEMPLATE_DIR": str(documents / "KiCad" / version) if documents else "",+        "KICAD_3RD_PARTY": str(Path(os.environ.get("APPDATA", "")) / "kicad" / version / "3rdparty") if os.environ.get("APPDATA") else "",+        f"KICAD{version.split('.')[0]}_SYMBOL_DIR": str(Path("C:/Program Files/KiCad") / version / "share" / "kicad" / "symbols"),+        f"KICAD{version.split('.')[0]}_FOOTPRINT_DIR": str(Path("C:/Program Files/KiCad") / version / "share" / "kicad" / "footprints"),+        f"KICAD{version.split('.')[0]}_3DMODEL_DIR": str(Path("C:/Program Files/KiCad") / version / "share" / "kicad" / "3dmodels"),+    }+    resolved = uri+    for var, val in substitutions.items():+        resolved = resolved.replace(f"${{{var}}}", val)+    # KIPRJMOD = the project dir of the currently-open project; can't be+    # resolved at detection time. Leave it as-is; the count probe will fail+    # gracefully if it ever shows up in an Adom entry (unlikely — Adom libs+    # aren't project-scoped).+    return Path(resolved)+++def _count_symbols_in_kicad_sym(sym_path: Path) -> int:+    """Count `(symbol "..."` entries in a .kicad_sym file. KiCad's symbol+    file format is s-expression with one or more `(symbol "Name" ...)` at+    the top level of `(kicad_symbol_lib ...)`.++    Returns 0 on any error (file missing, parse fail, etc.).+    """+    if not sym_path.exists():+        return 0+    try:+        with open(sym_path, "r", encoding="utf-8", errors="replace") as f:+            content = f.read()+    except OSError:+        return 0+    # Top-level `(symbol "Name"`. The kicad_sym format also has nested+    # `(symbol "Name_0_0" ...)` for unit/sub-symbol drawings — those start+    # with the parent symbol's name + underscore, so we filter them by+    # requiring the count to be at line-start indentation level (tab or 2+    # spaces). The simplest reliable approach: count `^\s*(symbol "..."` at+    # the beginning of a line. Top-level symbols typically have 1 leading+    # tab/2 spaces in pretty-printed output; nested ones have more.+    # Regex matches `(symbol "X"` where X is not just digits/underscore-only:+    matches = re.findall(r'^\s+\(symbol\s+"([^"]+)"', content, re.MULTILINE)+    # Filter out nested unit symbols (named like "ParentName_0_0" or+    # "ParentName_1_1"). These never appear as top-level entries in a+    # well-formed .kicad_sym, but Symbol Editor exports them with these+    # exact suffixes. Match: anything that ends with "_<digit>_<digit>".+    top_level = [n for n in matches if not re.search(r"_\d+_\d+$", n)]+    return len(top_level)+++def _count_footprints_in_pretty_dir(pretty_dir: Path) -> int:+    """Count .kicad_mod files in a KiCad footprint library directory.+    KiCad footprint libraries are directories (suffixed `.pretty/`) with+    one `.kicad_mod` file per footprint.+    """+    if not pretty_dir.exists() or not pretty_dir.is_dir():+        return 0+    try:+        return sum(1 for entry in pretty_dir.iterdir() if entry.suffix == ".kicad_mod")+    except OSError:+        return 0+++def _probe_ipc_api(python_exe: Path) -> dict:+    """Probe whether KiCad's IPC API (`kipy` module) is importable from the+    bundled python. KiCad 10 returns False everywhere; KiCad 11 (when it ships)+    is expected to bundle it. The probe is a one-shot subprocess that imports+    + prints a small structured result so we don't pollute stderr.++    Returns: {"available": bool, "scope": list[str]}.+    """+    if not python_exe.exists():+        return {"available": False, "scope": []}+    probe = (+        "import json,sys\n"+        "try:\n"+        "    import kipy\n"+        "    scope = []\n"+        "    for name in ('pcb', 'sch', 'sym', 'fp'):\n"+        "        if hasattr(kipy, name): scope.append(name)\n"+        "    json.dump({'available': True, 'scope': scope}, sys.stdout)\n"+        "except ImportError:\n"+        "    json.dump({'available': False, 'scope': []}, sys.stdout)\n"+    )+    try:+        result = subprocess.run(+            [str(python_exe), "-c", probe],+            capture_output=True, text=True, timeout=5,+        )+        if result.returncode != 0 or not result.stdout:+            return {"available": False, "scope": []}+        import json+        data = json.loads(result.stdout)+        return {+            "available": bool(data.get("available")),+            "scope": list(data.get("scope") or []),+        }+    except (subprocess.TimeoutExpired, OSError, ValueError):+        return {"available": False, "scope": []}+++def _win_registry_kicad_paths() -> list[Path]:+    """Read KiCad install paths from the Windows registry. Looks under+    HKLM\\SOFTWARE\\KiCad\\<version>\\InstallationPath (and the WOW6432Node+    mirror for 32-bit installs on 64-bit Windows). Returns version-dir+    Paths so the standard _win_build_info pipeline can take over."""+    if not IS_WINDOWS:+        return []+    try:+        import winreg  # stdlib on Windows; ImportError on others+    except ImportError:+        return []+    out: list[Path] = []+    for hive in (winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER):+        for subkey in (r"SOFTWARE\KiCad", r"SOFTWARE\WOW6432Node\KiCad"):+            try:+                with winreg.OpenKey(hive, subkey) as parent:+                    i = 0+                    while True:+                        try:+                            version = winreg.EnumKey(parent, i)+                        except OSError:+                            break+                        i += 1+                        try:+                            with winreg.OpenKey(parent, version) as ver_key:+                                # KiCad's installer writes "InstallationPath" or+                                # the unnamed default value. Try both.+                                for value_name in ("InstallationPath", "Install_Dir", ""):+                                    try:+                                        path_str, _ = winreg.QueryValueEx(ver_key, value_name)+                                        if path_str:+                                            out.append(Path(str(path_str).strip().strip('"')))+                                            break+                                    except OSError:+                                        continue+                        except OSError:+                            continue+            except OSError:+                continue+    return out+++def _win_detect_all() -> list[dict]:+    found: list[dict] = []+    seen: set[str] = set()++    def consider(version_dir: Path):+        """version_dir is something like C:/Program Files/KiCad/9.0/."""+        try:+            resolved = str(version_dir.resolve())+        except OSError:+            resolved = str(version_dir)+        if resolved in seen:+            return+        seen.add(resolved)+        # Pull the version name from the dir if it looks like X.Y.Z, otherwise+        # try to read it from kicad-cli; bail if neither works.+        name = version_dir.name+        if re.match(r"^\d+(\.\d+)*$", name):+            info = _win_build_info(name, base_override=version_dir)+        else:+            # Non-standard layout (registry-pointed install with no version+            # in the path). Fall back: probe bin/kicad-cli.exe --version.+            cli = version_dir / "bin" / "kicad-cli.exe"+            if not cli.exists():+                return+            try:+                result = subprocess.run([str(cli), "--version"], capture_output=True, text=True, timeout=5)+                m = re.search(r"(\d+\.\d+(?:\.\d+)?)", result.stdout + result.stderr)+                if not m:+                    return+                info = _win_build_info(m.group(1), base_override=version_dir.parent / m.group(1) if (version_dir.parent / m.group(1)).exists() else version_dir)+            except (subprocess.TimeoutExpired, OSError):+                return+        if info:+            found.append(info)++    # 1. Scan all known parent dirs for version subdirs.+    for parent in WIN_KICAD_BASES:+        if not parent.exists():+            continue+        try:+            for entry in parent.iterdir():+                if not entry.is_dir():+                    continue+                if not re.match(r"^\d+(\.\d+)*$", entry.name):+                    continue+                consider(entry)+        except (OSError, PermissionError):+            continue++    # 2. Scan registry for installs in non-standard locations.+    for reg_path in _win_registry_kicad_paths():+        if not reg_path.exists():+            continue+        # Registry value can point at the version dir directly (newer installs)+        # or at the parent (older installs). Handle both.+        if (reg_path / "bin" / "kicad.exe").exists():+            consider(reg_path)+        else:+            try:+                for entry in reg_path.iterdir():+                    if entry.is_dir() and re.match(r"^\d+(\.\d+)*$", entry.name):+                        consider(entry)+            except (OSError, PermissionError):+                continue++    found.sort(key=lambda info: _version_tuple(info["version"]), reverse=True)+    return found+++# ╔═══════════════════════════════════════════════════════════════════════════╗+# ║                                macOS                                       ║+# ╚═══════════════════════════════════════════════════════════════════════════╝++def _mac_app_version(app_path: Path) -> str | None:+    """Read CFBundleShortVersionString from an app's Info.plist.+    Returns the version string ('9.0.0', '8.0.4') or None on failure."""+    plist = app_path / "Contents" / "Info.plist"+    if not plist.exists():+        return None+    try:+        result = subprocess.run(+            ["/usr/libexec/PlistBuddy", "-c", "Print :CFBundleShortVersionString", str(plist)],+            capture_output=True, text=True, timeout=5,+        )+        if result.returncode == 0:+            return result.stdout.strip() or None+    except (subprocess.TimeoutExpired, FileNotFoundError):+        pass+    return None+++def _mac_short_version(full_version: str) -> str:+    """'9.0.0' -> '9.0'  (KiCad's config/user dirs use major.minor only)."""+    parts = full_version.split(".")+    if len(parts) >= 2:+        return f"{parts[0]}.{parts[1]}"+    return full_version+++def _mac_find_config_dir(short_version: str):+    """KiCad config dir on macOS: ~/Library/Preferences/kicad/<X.Y>/"""+    home = Path.home()+    config = home / "Library" / "Preferences" / "kicad" / short_version+    return config if config.exists() else None+++def _mac_find_user_dir(short_version: str):+    """KiCad user dir on macOS: ~/Documents/KiCad/<X.Y>/"""+    home = Path.home()+    user = home / "Documents" / "KiCad" / short_version+    # We can't always stat ~/Documents under the macOS Privacy layer (TCC),+    # but the path is conventional. Return it unconditionally and let callers+    # handle missing dirs gracefully.+    return user+++def _mac_build_info(app_path: Path) -> dict | None:+    """Build the kicad_info dict for a macOS KiCad.app bundle, or None+    if it doesn't look like a real install."""+    bin_dir = app_path / "Contents" / "MacOS"+    kicad_exe = bin_dir / "kicad"+    if not kicad_exe.exists():+        return None++    full_version = _mac_app_version(app_path)+    if not full_version:+        return None+    short_version = _mac_short_version(full_version)++    # Sibling editor apps next to the main bundle (PCB Editor.app, Schematic Editor.app, etc.).+    # KiCad ships these alongside KiCad.app under /Applications/KiCad/. The actual+    # binaries inside each bundle are Contents/MacOS/<name> — directly Popen-able+    # by handlers that were written for Windows .exe paths.+    parent = app_path.parent+    pcbnew_bin = parent / "PCB Editor.app" / "Contents" / "MacOS" / "pcbnew"+    eeschema_bin = parent / "Schematic Editor.app" / "Contents" / "MacOS" / "eeschema"++    config_dir = _mac_find_config_dir(short_version)+    user_dir = _mac_find_user_dir(short_version)++    # kicad-cli ships in the main bundle's MacOS dir on macOS+    kicad_cli = bin_dir / "kicad-cli"++    return {+        "installed": True,+        "platform": "macos",+        # Use major.minor for the version key so it matches the Windows convention+        # ('9.0' not '9.0.0') and lines up with config/user dir names.+        "version": short_version,+        "full_version": full_version,+        "base_dir": str(app_path),+        "bin_dir": str(bin_dir),+        # Direct path to the kicad project-manager binary+        "kicad_exe": str(kicad_exe),+        # Path to the editor binaries inside their bundles. Handlers can Popen+        # these directly (same shape as Windows .exe paths). Falls back to the+        # main kicad binary if the editor bundle is missing.+        "pcbnew_exe": str(pcbnew_bin) if pcbnew_bin.exists() else str(kicad_exe),+        "eeschema_exe": str(eeschema_bin) if eeschema_bin.exists() else str(kicad_exe),+        "kicad_cli_exe": str(kicad_cli) if kicad_cli.exists() else None,+        # macOS bundles ship their own embedded Python under Frameworks/Python.framework;+        # plugin code that needs to invoke it should call `kicad-cli` instead.+        "python_exe": None,+        "config_dir": str(config_dir) if config_dir else None,+        "user_dir": str(user_dir) if user_dir else None,+        "sym_lib_table": str(config_dir / "sym-lib-table") if config_dir else None,+        "fp_lib_table": str(config_dir / "fp-lib-table") if config_dir else None,+        "user_symbols_dir": str(user_dir / "symbols") if user_dir else None,+        "user_footprints_dir": str(user_dir / "footprints") if user_dir else None,+        "user_3dmodels_dir": str(user_dir / "3dmodels") if user_dir else None,+    }+++def _mac_detect_all() -> list[dict]:+    """Discover every KiCad install on macOS.++    Looks first inside /Applications/KiCad/ (where the official installer puts+    the bundle), then falls back to scanning /Applications/ directly for+    KiCad*.app — covers users who drag the bundle straight into Applications.+    """+    found: list[dict] = []+    seen: set[str] = set()++    candidates: list[Path] = []+    if MAC_KICAD_DIR.exists():+        for entry in MAC_KICAD_DIR.iterdir():+            if entry.suffix == ".app" and entry.name.lower().startswith("kicad"):+                candidates.append(entry)+    if MAC_APPS_DIR.exists():+        try:+            for entry in MAC_APPS_DIR.iterdir():+                if entry.suffix == ".app" and entry.name.lower().startswith("kicad"):+                    candidates.append(entry)+        except PermissionError:+            pass++    for app in candidates:+        key = str(app.resolve())+        if key in seen:+            continue+        seen.add(key)+        info = _mac_build_info(app)+        if info:+            found.append(info)++    found.sort(key=lambda info: _version_tuple(info["version"]), reverse=True)+    return found+++# ╔═══════════════════════════════════════════════════════════════════════════╗+# ║                          PUBLIC API (cross-platform)                       ║+# ╚═══════════════════════════════════════════════════════════════════════════╝++def detect_all_kicad_versions() -> list[dict]:+    """Discover every installed KiCad version on the current platform.+    Returns a list sorted newest first. Empty list if KiCad isn't installed."""+    if IS_WINDOWS:+        return _win_detect_all()+    if IS_MACOS:+        return _mac_detect_all()+    return []+++def detect_kicad(version: str | None = None) -> dict:+    """Detect a specific KiCad install (by version) or the latest installed.++    - `version=None`: return the newest installed version+    - `version="9.0"`: return that exact version, or {installed:false} with a hint+      listing all available versions if not found.++    Backward-compatible with the original (no-arg) behavior — existing call sites+    that do `detect_kicad()` still get the latest install.+    """+    all_versions = detect_all_kicad_versions()++    if not all_versions:+        if IS_WINDOWS:+            scanned = ", ".join(str(p) for p in WIN_KICAD_BASES)+            err = (+                f"KiCad not found in any of: {scanned}, or in the Windows registry "+                f"(HKLM/HKCU\\SOFTWARE\\KiCad). Install from https://www.kicad.org/download/, "+                f"or run `adom-desktop desktop_install_kicad` for an unattended winget install (v1.6.0+)."+            )+            hint = (+                "Trigger an unattended install with `adom-desktop desktop_install_kicad '{}'`. "+                "It runs `winget install --id KiCad.KiCad --silent --accept-package-agreements "+                "--accept-source-agreements`. Requires winget (Windows 10 1809+ / Windows 11). "+                "Takes ~2-5 min depending on network. Re-run kicad_list_versions after to verify."+            )+        elif IS_MACOS:+            err = "KiCad not found in /Applications/. Install from https://www.kicad.org/download/macos/."+            hint = "On macOS the install is manual — download the KiCad installer DMG from kicad.org."+        else:+            err = f"KiCad detection is not implemented for platform {sys.platform!r}."+            hint = None+        result = {+            "installed": False,+            "platform": _current_platform(),+            "version": None,+            "available_versions": [],+            "error": err,+            "errorCode": "kicad_not_installed",+        }+        if hint:+            result["_hint"] = hint+        return result++    if version is None:+        return all_versions[0]++    # Try exact match, then prefix match (e.g. "9" matches "9.0")+    for info in all_versions:+        if info["version"] == version:+            return info+    for info in all_versions:+        if info["version"].startswith(f"{version}."):+            return info++    available = [v["version"] for v in all_versions]+    return {+        "installed": False,+        "platform": _current_platform(),+        "version": version,+        "available_versions": available,+        "error": f"KiCad {version} is not installed. Available versions: {', '.join(available)}.",+        "_hint": f"Pass kicadVersion as one of: {', '.join(available)}, or omit it to use the newest ({available[0]}).",+    }+++def _current_platform() -> str:+    if IS_WINDOWS:+        return "windows"+    if IS_MACOS:+        return "macos"+    return sys.platform+++if __name__ == "__main__":+    import json+    all_v = detect_all_kicad_versions()+    print(json.dumps({+        "platform": _current_platform(),+        "all_versions": all_v,+        "latest": all_v[0] if all_v else None,+    }, indent=2))
launch_editor.ps1added+118
@@ -0,0 +1,118 @@+# Launch a specific editor from the KiCad project manager using UI Automation.+# Usage: launch_editor.ps1 -EditorName "Footprint Editor"+# Also supports: "Symbol Editor", "PCB Editor", "Schematic Editor", "Gerber Viewer", etc.+param(+    [Parameter(Mandatory=$true)]+    [string]$EditorName,+    [Parameter(Mandatory=$false)]+    [long]$KiCadHwnd = 0+)++Add-Type -AssemblyName UIAutomationClient+Add-Type -AssemblyName UIAutomationTypes+Add-Type -AssemblyName System.Windows.Forms++Add-Type @"+using System;+using System.Runtime.InteropServices;++public class Win32Launch {+    [DllImport("user32.dll")]+    public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);+    [DllImport("user32.dll")]+    public static extern bool SetForegroundWindow(IntPtr hWnd);+    [DllImport("user32.dll")]+    public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);+    [DllImport("user32.dll")]+    public static extern IntPtr GetForegroundWindow();+}+"@++# If no HWND provided, find KiCad window+if ($KiCadHwnd -eq 0) {+    $procs = Get-Process -Name "kicad" -ErrorAction SilentlyContinue+    if ($procs) {+        $KiCadHwnd = $procs[0].MainWindowHandle.ToInt64()+    }+    if ($KiCadHwnd -eq 0) {+        Write-Output "ERROR:KiCad not found"+        exit 1+    }+}++$hwnd = [IntPtr]$KiCadHwnd++# Alt-key trick for foreground permission+[Win32Launch]::keybd_event(0x12, 0, 0, [UIntPtr]::Zero)+[Win32Launch]::keybd_event(0x12, 0, 2, [UIntPtr]::Zero)+Start-Sleep -Milliseconds 50++# Bring KiCad to foreground+[Win32Launch]::ShowWindow($hwnd, 9) | Out-Null+[Win32Launch]::SetForegroundWindow($hwnd) | Out-Null+Start-Sleep -Milliseconds 500++# Use UI Automation to find the launcher text+$root = [System.Windows.Automation.AutomationElement]::FromHandle($hwnd)+if (-not $root) {+    Write-Output "ERROR:Cannot get UI Automation element for KiCad"+    exit 1+}++# Search for a Text element matching the editor name+$condition = New-Object System.Windows.Automation.PropertyCondition(+    [System.Windows.Automation.AutomationElement]::NameProperty,+    $EditorName+)++$element = $root.FindFirst(+    [System.Windows.Automation.TreeScope]::Descendants,+    $condition+)++if (-not $element) {+    Write-Output "ERROR:Could not find '$EditorName' in KiCad UI"+    exit 1+}++# Try to get an InvokePattern (clickable element)+try {+    $invokePattern = $element.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern)+    $invokePattern.Invoke()+    Write-Output "OK:Invoked $EditorName via InvokePattern"+    exit 0+} catch {+    # No InvokePattern — this is a text label, not a button.+    # Fall back to clicking at the element's location.+}++# Get the element's bounding rectangle and click on it+$rect = $element.Current.BoundingRectangle+if ($rect.IsEmpty) {+    Write-Output "ERROR:Element has no bounding rectangle"+    exit 1+}++$clickX = [int]($rect.X + $rect.Width / 2)+$clickY = [int]($rect.Y + $rect.Height / 2)++# Use SendKeys mouse simulation: move cursor and click+[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point($clickX, $clickY)+Start-Sleep -Milliseconds 100++# Simulate click+Add-Type @"+using System;+using System.Runtime.InteropServices;++public class MouseClick {+    [DllImport("user32.dll")]+    public static extern void mouse_event(uint dwFlags, int dx, int dy, uint dwData, UIntPtr dwExtraInfo);+}+"@++[MouseClick]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero)  # LEFTDOWN+Start-Sleep -Milliseconds 30+[MouseClick]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero)  # LEFTUP++Write-Output "OK:Clicked at ($clickX, $clickY) on $EditorName"
lib_table.pyadded+136
@@ -0,0 +1,136 @@+"""S-expression parser/writer for KiCad library table files (sym-lib-table, fp-lib-table)."""++import re+from pathlib import Path+++class LibEntry:+    """Represents a single library entry in a lib table."""++    def __init__(self, name: str, lib_type: str, uri: str,+                 options: str = "", descr: str = ""):+        self.name = name+        self.lib_type = lib_type+        self.uri = uri+        self.options = options+        self.descr = descr++    def to_sexpr(self) -> str:+        return (+            f'  (lib (name "{self.name}")'+            f'(type "{self.lib_type}")'+            f'(uri "{self.uri}")'+            f'(options "{self.options}")'+            f'(descr "{self.descr}"))'+        )+++class LibTable:+    """Represents a KiCad sym-lib-table or fp-lib-table."""++    def __init__(self, table_type: str):+        """table_type is 'sym_lib_table' or 'fp_lib_table'."""+        self.table_type = table_type+        self.version: int | None = None  # KiCad 9+ uses (version 7)+        self.entries: list = []++    @classmethod+    def parse_file(cls, filepath: str) -> "LibTable":+        """Parse a lib table file. Creates empty table if file doesn't exist."""+        path = Path(filepath)++        if not path.exists():+            if "sym" in path.name:+                t = cls("sym_lib_table")+            else:+                t = cls("fp_lib_table")+            t.version = 7  # KiCad 9+ expects (version 7)+            return t++        text = path.read_text(encoding="utf-8")+        return cls.parse_string(text)++    @classmethod+    def parse_string(cls, text: str) -> "LibTable":+        """Parse S-expression text into a LibTable."""+        m = re.match(r'\s*\((\w+)', text)+        if not m:+            raise ValueError("Cannot parse lib table: no root expression found")++        table = cls(m.group(1))++        # Parse (version N) if present (KiCad 9+)+        ver_match = re.search(r'\(version\s+(\d+)\)', text)+        if ver_match:+            table.version = int(ver_match.group(1))++        # Match each (lib ...) entry+        lib_pattern = re.compile(+            r'\(lib\s+'+            r'\(name\s+"([^"]*)"\)'+            r'\s*\(type\s+"([^"]*)"\)'+            r'\s*\(uri\s+"([^"]*)"\)'+            r'\s*\(options\s+"([^"]*)"\)'+            r'\s*\(descr\s+"([^"]*)"\)'+            r'\s*\)',+            re.DOTALL+        )++        for m in lib_pattern.finditer(text):+            entry = LibEntry(+                name=m.group(1),+                lib_type=m.group(2),+                uri=m.group(3),+                options=m.group(4),+                descr=m.group(5),+            )+            table.entries.append(entry)++        return table++    def has_library(self, name: str) -> bool:+        """Check if a library with the given name exists."""+        return any(e.name == name for e in self.entries)++    def add_library(self, entry: LibEntry) -> bool:+        """Add a library entry. Returns False if name already exists."""+        if self.has_library(entry.name):+            return False+        self.entries.append(entry)+        return True++    def remove_library(self, name: str) -> bool:+        """Remove a library by name. Returns True if found and removed."""+        before = len(self.entries)+        self.entries = [e for e in self.entries if e.name != name]+        return len(self.entries) < before++    def to_string(self) -> str:+        """Serialize back to S-expression format."""+        lines = [f"({self.table_type}"]+        if self.version is not None:+            lines.append(f"  (version {self.version})")+        for entry in self.entries:+            lines.append(entry.to_sexpr())+        lines.append(")")+        return "\n".join(lines) + "\n"++    def write_file(self, filepath: str):+        """Write the table to a file, creating parent dirs if needed."""+        path = Path(filepath)+        path.parent.mkdir(parents=True, exist_ok=True)+        path.write_text(self.to_string(), encoding="utf-8")+++if __name__ == "__main__":+    import sys+    if len(sys.argv) > 1:+        table = LibTable.parse_file(sys.argv[1])+        print(f"Table type: {table.table_type}")+        print(f"Entries: {len(table.entries)}")+        for e in table.entries[:5]:+            print(f"  - {e.name}: {e.uri}")+        if len(table.entries) > 5:+            print(f"  ... and {len(table.entries) - 5} more")+    else:+        print("Usage: python lib_table.py <path-to-lib-table-file>")
navigate_symbol.ps1added+133
@@ -0,0 +1,133 @@+param(+    [Parameter(Mandatory=$true)]+    [long]$SymEditorHwnd,+    [Parameter(Mandatory=$true)]+    [long]$SearchEditHwnd,+    [Parameter(Mandatory=$true)]+    [string]$SymbolName+)++Add-Type -AssemblyName System.Windows.Forms++Add-Type @"+using System;+using System.Runtime.InteropServices;+using System.Text;++public class Win32Nav {+    [DllImport("user32.dll")]+    public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);+    [DllImport("user32.dll")]+    public static extern bool SetForegroundWindow(IntPtr hWnd);+    [DllImport("user32.dll")]+    public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);+    [DllImport("user32.dll")]+    public static extern IntPtr GetForegroundWindow();+    [DllImport("user32.dll")]+    public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);+    [DllImport("user32.dll")]+    public static extern bool SetCursorPos(int X, int Y);+    [DllImport("user32.dll")]+    public static extern void mouse_event(uint dwFlags, int dx, int dy, uint dwData, UIntPtr dwExtraInfo);+    [DllImport("user32.dll")]+    public static extern int GetWindowTextW(IntPtr hWnd, StringBuilder sb, int max);+    [DllImport("user32.dll")]+    public static extern int GetWindowTextLengthW(IntPtr hWnd);++    [StructLayout(LayoutKind.Sequential)]+    public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }+}+"@++$hwnd = [IntPtr]$SymEditorHwnd+$searchEdit = [IntPtr]$SearchEditHwnd++# Get search box screen coordinates+$rect = New-Object Win32Nav+RECT+[Win32Nav]::GetWindowRect($searchEdit, [ref]$rect) | Out-Null+$clickX = [int](($rect.Left + $rect.Right) / 2)+$clickY = [int](($rect.Top + $rect.Bottom) / 2)++if ($clickX -eq 0 -and $clickY -eq 0) {+    Write-Output "ERROR:Search edit has zero coordinates"+    exit 1+}++# Wrap everything in try/finally to ensure modifier keys are ALWAYS released.+# A stuck Alt/Ctrl/Shift key breaks Alt+Tab and window focus on the desktop.+try {++# Alt-key trick to gain foreground permission+[Win32Nav]::keybd_event(0x12, 0, 0, [UIntPtr]::Zero)+[Win32Nav]::keybd_event(0x12, 0, 2, [UIntPtr]::Zero)+Start-Sleep -Milliseconds 50++# Bring Symbol Editor to foreground+[Win32Nav]::ShowWindow($hwnd, 9) | Out-Null+[Win32Nav]::SetForegroundWindow($hwnd) | Out-Null+Start-Sleep -Milliseconds 300++# Verify foreground+$fg = [Win32Nav]::GetForegroundWindow()+if ($fg -ne $hwnd) {+    Write-Output "ERROR:Could not get foreground focus"+    exit 1+}++# Click on the search box to give it input focus+[Win32Nav]::SetCursorPos($clickX, $clickY) | Out-Null+Start-Sleep -Milliseconds 50+[Win32Nav]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero)  # LEFTDOWN+Start-Sleep -Milliseconds 30+[Win32Nav]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero)  # LEFTUP+Start-Sleep -Milliseconds 200++# Select all + delete existing text+[System.Windows.Forms.SendKeys]::SendWait("^a")+Start-Sleep -Milliseconds 50+[System.Windows.Forms.SendKeys]::SendWait("{DELETE}")+Start-Sleep -Milliseconds 150++# Type the symbol name (SendKeys escaping for special chars)+$escaped = $SymbolName -replace '([+^%~{}()])', '{$1}'+[System.Windows.Forms.SendKeys]::SendWait($escaped)+Start-Sleep -Milliseconds 500++# Press Enter to trigger the search/filter in the tree+[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")+Start-Sleep -Milliseconds 1000++# Send Ctrl+Shift+E to open the filtered symbol+[System.Windows.Forms.SendKeys]::SendWait("^+e")+Start-Sleep -Milliseconds 1500++} finally {+    # ALWAYS release all modifier keys to prevent stuck-key issues+    [Win32Nav]::keybd_event(0x12, 0, 2, [UIntPtr]::Zero)  # Alt up+    [Win32Nav]::keybd_event(0xA4, 0, 2, [UIntPtr]::Zero)  # Left Alt up+    [Win32Nav]::keybd_event(0xA5, 0, 2, [UIntPtr]::Zero)  # Right Alt up+    [Win32Nav]::keybd_event(0x11, 0, 2, [UIntPtr]::Zero)  # Ctrl up+    [Win32Nav]::keybd_event(0xA2, 0, 2, [UIntPtr]::Zero)  # Left Ctrl up+    [Win32Nav]::keybd_event(0xA3, 0, 2, [UIntPtr]::Zero)  # Right Ctrl up+    [Win32Nav]::keybd_event(0x10, 0, 2, [UIntPtr]::Zero)  # Shift up+    [Win32Nav]::keybd_event(0xA0, 0, 2, [UIntPtr]::Zero)  # Left Shift up+    [Win32Nav]::keybd_event(0xA1, 0, 2, [UIntPtr]::Zero)  # Right Shift up+}++# Check if the symbol loaded+$len = [Win32Nav]::GetWindowTextLengthW($hwnd)+if ($len -gt 0) {+    $sb = New-Object System.Text.StringBuilder($len + 1)+    [Win32Nav]::GetWindowTextW($hwnd, $sb, $len + 1) | Out-Null+    $title = $sb.ToString()++    if ($title -match [regex]::Escape($SymbolName)) {+        Write-Output "OK:$title"+    } elseif ($title -notmatch "no symbol loaded") {+        Write-Output "PARTIAL:$title"+    } else {+        Write-Output "NOLOAD:$title"+    }+} else {+    Write-Output "NOLOAD:Could not read title"+}
plugin_install.pyadded+528
@@ -0,0 +1,528 @@+"""plugin_install.py — auto-install adom_bridge into KiCad USER_SITE.++The Phase 2 reverse bridge plugin (`adom_bridge.py`) needs to live at+KiCad's USER_SITE directory so that KiCad's bundled Python loads it via+`usercustomize.py` at every GUI process startup. See PLAN.md v4 for the+architectural story.++This module does the deployment, with extensive feedback for the AI that+triggered the install. The pattern is:+- On every bridge boot, the first incoming kicad_* command triggers+  `ensure_plugin_installed_once()`.+- The result (whether install was needed, what was copied, what failed)+  is cached in module state and attached as `pluginAutoInstall` on that+  first response.+- Subsequent commands do nothing (cached short-circuit) so steady-state+  command flow has zero overhead.++Failure modes the AI needs to know about+========================================+* `appdata_missing` — %USERPROFILE% (or $HOME on Mac) unresolved.+* `no_kicad_installed` — kicad_detect found zero installs, nothing to deploy to.+* `payload_missing` — plugins/kicad/plugin_payload/ wasn't bundled into+  the Tauri app (Tauri bundling regression, like the v1.3.30..v1.3.37+  recorder.html bug — see verify_bundle.py).+* `file_locked` — the destination .py was open (rare; might happen if+  KiCad already has it imported and OS locks the file on Windows).+* `permission_denied` — user-site dir uncreatable (very rare).+* `kicad_running` — KiCad was running when we installed; the running+  process WON'T pick up the new plugin until restarted.++Every error case returns a `_hint` field telling the calling AI exactly+what action it should take to recover.+"""++from __future__ import annotations++import hashlib+import json+import os+import platform+import shutil+import sys+import threading+import traceback+from datetime import datetime+from pathlib import Path+from typing import Optional++# Bridge version we're shipping. Bumped together with adom_bridge.py's+# __version__ — version drift between this file and the payload would+# cause an infinite reinstall loop; the runtime asserts they match.+PAYLOAD_VERSION = "0.7.0"++# Module-level cache: install runs at most ONCE per bridge process boot.+# Multiple concurrent kicad_* commands hitting the bridge during startup+# might all check `ensure_plugin_installed_once()` — the lock guarantees+# only the first one actually does the file copy.+_install_lock = threading.Lock()+_install_result: Optional[dict] = None+_response_attached = False+++def _payload_dir() -> Path:+    """Find plugin_payload/ relative to this file. Works in dev (`plugins/kicad/`)+    AND in the bundled Tauri release (`<resources>/plugins/kicad/`)."""+    return Path(__file__).parent / "plugin_payload"+++def _user_site_dir_windows(kicad_version: str) -> Optional[Path]:+    """Compute the KiCad USER_SITE path on Windows.++    KiCad's bundled sitecustomize.py builds this path from `CSIDL_PERSONAL`+    (== `%USERPROFILE%\\Documents`) joined with `KiCad\\<version>\\3rdparty\\Python<XY>\\site-packages`.+    We're matching that exactly so adom_bridge.py lands where KiCad's+    embedded interpreter will look for usercustomize.py.++    Returns None if USERPROFILE isn't set (extremely unusual on Windows).+    """+    userprofile = os.environ.get("USERPROFILE")+    if not userprofile:+        return None+    # KiCad 10 uses Python 3.11 — the path includes the no-dot version+    # ("311"). KiCad 9 also used 3.x; we hardcode 311 for now and add a+    # version detection step in Phase 2.x.+    return Path(userprofile) / "Documents" / "KiCad" / kicad_version / "3rdparty" / "Python311" / "site-packages"+++def _user_site_dir_mac(kicad_version: str) -> Optional[Path]:+    """Best-effort macOS USER_SITE. NEEDS EMPIRICAL VERIFICATION+    (Phase 1 macOS parity follow-up). Documented here so future Kyle-+    session work can confirm/correct."""+    home = os.environ.get("HOME")+    if not home:+        return None+    return Path(home) / "Library" / "Preferences" / "kicad" / kicad_version / "3rdparty" / "Python311" / "site-packages"+++def _user_site_dir(kicad_version: str) -> Optional[Path]:+    if sys.platform == "win32":+        return _user_site_dir_windows(kicad_version)+    if sys.platform == "darwin":+        return _user_site_dir_mac(kicad_version)+    # Linux KiCad install paths vary wildly (flatpak vs apt vs build-+    # from-source). Caller will get "skipped_unsupported_platform".+    return None+++def _file_hash(path: Path) -> Optional[str]:+    try:+        with open(path, "rb") as f:+            return hashlib.sha256(f.read()).hexdigest()+    except OSError:+        return None+++def _file_state(dest: Path) -> dict:+    """Inspect a destination file and return state for the feedback report.++    Returns a dict with `exists`, `size`, `sha256`, `mtime` (or `_error`).+    """+    if not dest.exists():+        return {"exists": False}+    try:+        st = dest.stat()+        return {+            "exists": True,+            "size": st.st_size,+            "sha256": _file_hash(dest),+            "mtime": datetime.utcfromtimestamp(st.st_mtime).isoformat() + "Z",+        }+    except OSError as e:+        return {"exists": True, "_error": f"stat failed: {e}"}+++def _is_kicad_running() -> list[dict]:+    """Best-effort: return PIDs of running KiCad GUI exes. So the AI can+    warn the user that the freshly-installed plugin won't be active until+    they restart KiCad."""+    if sys.platform != "win32":+        return []+    try:+        import subprocess+        out = subprocess.run(+            ["tasklist", "/FO", "CSV", "/NH"],+            capture_output=True, text=True, timeout=10,+        )+        running: list[dict] = []+        targets = {"kicad.exe", "pcbnew.exe", "eeschema.exe", "kicad-cli.exe"}+        for line in out.stdout.splitlines():+            parts = [p.strip('"') for p in line.split(",")]+            if len(parts) < 2:+                continue+            name = parts[0].lower()+            if name in targets:+                try:+                    running.append({"exeName": name, "pid": int(parts[1])})+                except ValueError:+                    pass+        return running+    except (OSError, subprocess.SubprocessError):+        return []+++def _install_one_version(version: str, version_info: dict) -> dict:+    """Install / refresh the plugin for ONE KiCad version. Returns a+    dict describing what happened."""+    report: dict = {+        "kicadVersion": version,+        "installPath": version_info.get("base_dir"),+    }+    payload = _payload_dir()+    if not payload.exists():+        report["action"] = "failed"+        report["errorCode"] = "payload_missing"+        report["error"] = f"plugin payload directory not found at {payload}"+        report["_hint"] = (+            "The plugin_payload/ directory wasn't shipped with the bundle. This is a "+            "build regression — see verify_bundle.py in src-tauri/. Report to the user "+            "and ask them to upgrade adom-desktop to a build that includes the payload "+            "(verify with: adom-desktop --version, expecting v1.7.5 or newer)."+        )+        return report++    user_site = _user_site_dir(version)+    if user_site is None:+        report["action"] = "skipped"+        report["errorCode"] = "user_site_unavailable"+        report["error"] = (+            f"could not compute USER_SITE path on {sys.platform} for KiCad {version} "+            f"(missing USERPROFILE / HOME?)"+        )+        report["_hint"] = (+            f"Plugin auto-install is only supported on Windows in v1.7.x; "+            f"this is {sys.platform}. macOS parity is Phase 1 follow-up. "+            "Other kicad_* commands still work via the legacy UI-automation path; "+            "you just won't have the in-process reverse bridge."+        )+        return report++    report["userSitePath"] = str(user_site)+    src_bridge = payload / "adom_bridge.py"+    src_loader = payload / "usercustomize.py"+    dest_bridge = user_site / "adom_bridge.py"+    dest_loader = user_site / "usercustomize.py"++    # Pre-flight: inspect current state so feedback shows before/after+    pre_state = {+        "adom_bridge.py": _file_state(dest_bridge),+        "usercustomize.py": _file_state(dest_loader),+    }+    report["preState"] = pre_state++    # Compare hashes — short-circuit if already up to date+    src_bridge_hash = _file_hash(src_bridge)+    src_loader_hash = _file_hash(src_loader)+    dest_bridge_hash = pre_state["adom_bridge.py"].get("sha256")+    dest_loader_hash = pre_state["usercustomize.py"].get("sha256")++    if dest_bridge_hash == src_bridge_hash and dest_loader_hash == src_loader_hash:+        report["action"] = "already-current"+        report["payloadVersion"] = PAYLOAD_VERSION+        report["message"] = (+            f"plugin already deployed at {user_site} and content matches the shipped "+            f"payload (sha256 identical for both files). No write performed."+        )+        return report++    # Mkdir the user-site if missing. The KiCad sitecustomize.py creates+    # this on first launch too, so we're paving the same path it would.+    try:+        user_site.mkdir(parents=True, exist_ok=True)+    except OSError as e:+        report["action"] = "failed"+        report["errorCode"] = "permission_denied"+        report["error"] = f"could not create {user_site}: {e}"+        report["_hint"] = (+            "Filesystem permission issue creating the KiCad user-site directory. "+            "This is rare on Windows. Possible causes: (1) Documents folder is on "+            "OneDrive with sync errors; (2) antivirus blocking. Report exact error "+            "to the user and have them try: `mkdir -p '<userSitePath>'` manually, "+            "then retry the kicad_* command. If that also fails, the user has a "+            "broken filesystem state."+        )+        return report++    # Copy. shutil.copy2 preserves timestamps which is harmless. We do+    # one file at a time so a half-failure still leaves a coherent state.+    written: list[dict] = []+    for src, dest in ((src_bridge, dest_bridge), (src_loader, dest_loader)):+        try:+            shutil.copy2(src, dest)+            written.append({+                "name": src.name,+                "src": str(src),+                "dest": str(dest),+                "size": dest.stat().st_size,+                "sha256": _file_hash(dest),+            })+        except OSError as e:+            report["action"] = "failed"+            report["errorCode"] = "copy_failed"+            report["error"] = f"copying {src.name} -> {dest}: {e}"+            report["filesWritten"] = written+            report["_hint"] = (+                "Failed mid-copy. Most likely cause on Windows: the destination .py "+                "is currently imported by a running KiCad process which has Windows-"+                "level locks on the file. Fix: ask the user to close ALL KiCad windows "+                "(Project Manager, PCB Editor, Schematic Editor), then retry the "+                "kicad_* command. If the file isn't held, this could also be an "+                "antivirus quarantine — check Windows Defender's protection history."+            )+            return report++    report["action"] = "installed" if not pre_state["adom_bridge.py"]["exists"] else "updated"+    report["payloadVersion"] = PAYLOAD_VERSION+    report["filesWritten"] = written+    report["message"] = (+        f"plugin {'installed' if report['action'] == 'installed' else 'updated to v' + PAYLOAD_VERSION} "+        f"at {user_site}. KiCad GUI processes launched AFTER this point will auto-load it via "+        f"sitecustomize.py + usercustomize.py."+    )+    return report+++def _do_install(all_kicad_versions: list[dict]) -> dict:+    """Internal entry. Iterates over every detected KiCad version and+    installs into each. Returns a top-level summary dict."""+    started_at = datetime.utcnow().isoformat() + "Z"++    if not all_kicad_versions:+        return {+            "triggered": True,+            "startedAt": started_at,+            "completedAt": datetime.utcnow().isoformat() + "Z",+            "summary": "no-kicad",+            "errorCode": "no_kicad_installed",+            "perVersion": [],+            "_hint": (+                "Auto-install skipped because kicad_detect found zero KiCad installs. "+                "Report this to the user; recommend installing KiCad from "+                "https://www.kicad.org/download/ (or via `adom-desktop desktop_install_kicad` "+                "which uses winget). After install, the next kicad_* command will retry "+                "the auto-install for you."+            ),+        }++    running = _is_kicad_running()+    per_version = []+    overall_action = "already-current"+    overall_error = None+    for v in all_kicad_versions:+        version = v.get("version") or "10.0"+        try:+            per_version.append(_install_one_version(version, v))+        except Exception as e:  # pylint: disable=broad-except+            per_version.append({+                "kicadVersion": version,+                "action": "failed",+                "errorCode": "unexpected_error",+                "error": f"{type(e).__name__}: {e}",+                "_traceback": traceback.format_exc(),+                "_hint": (+                    "Unexpected exception during plugin install for this version. "+                    "Full traceback in `_traceback`. Report to John on the adom-desktop "+                    "side; this should not happen and is a real bug, not an environment "+                    "issue."+                ),+            })++    # Pick the most-severe action across all versions as the overall summary.+    actions = [pv.get("action") for pv in per_version]+    if any(a == "failed" for a in actions):+        overall_action = "partial-failure"+        overall_error = next(pv for pv in per_version if pv.get("action") == "failed").get("errorCode")+    elif any(a == "installed" for a in actions):+        overall_action = "installed"+    elif any(a == "updated" for a in actions):+        overall_action = "updated"+    elif any(a == "skipped" for a in actions):+        overall_action = "skipped"++    result: dict = {+        "triggered": True,+        "startedAt": started_at,+        "completedAt": datetime.utcnow().isoformat() + "Z",+        "summary": overall_action,+        "payloadVersion": PAYLOAD_VERSION,+        "perVersion": per_version,+        "runningKiCad": running,+    }++    if overall_error:+        result["errorCode"] = overall_error++    # Top-level hint depends on what happened + whether KiCad was running+    if overall_action == "installed":+        if running:+            running_summary = ", ".join(f"{r['exeName']} pid={r['pid']}" for r in running)+            result["_hint"] = (+                f"Plugin successfully INSTALLED for the first time. HOWEVER, KiCad is "+                f"currently running ({running_summary}). Those running processes will "+                f"NOT pick up the new plugin (usercustomize.py only fires at Python "+                f"interpreter init, which happens at KiCad startup). "+                f"\n\nTo use the new reverse bridge, ask the user to: (1) Save any work "+                f"in their open KiCad windows; (2) Close ALL KiCad windows; (3) Reopen "+                f"KiCad. After that, run kicad_bridge_status to verify the plugin came "+                f"online.\n\nIf the user doesn't want to restart KiCad right now, the "+                f"existing non-bridge commands (open_board, install_symbol, etc.) keep "+                f"working through the legacy UI-automation path. The bridge is purely "+                f"additive."+            )+        else:+            result["_hint"] = (+                "Plugin successfully INSTALLED. No KiCad processes were running at "+                "install time, so the next time the user (or your next kicad_* command) "+                "launches KiCad, the reverse-bridge plugin will auto-load. Verify with "+                "kicad_bridge_status once KiCad is up."+            )+    elif overall_action == "updated":+        if running:+            result["_hint"] = (+                "Plugin successfully UPDATED to v" + PAYLOAD_VERSION + ". Running "+                "KiCad processes still have the OLD plugin loaded in-memory. They'll "+                "switch to the new version on next restart. Not urgent — current "+                "session still works."+            )+        else:+            result["_hint"] = (+                "Plugin successfully UPDATED to v" + PAYLOAD_VERSION + ". Next KiCad "+                "launch picks it up automatically."+            )+    elif overall_action == "already-current":+        result["_hint"] = (+            "Plugin already current at v" + PAYLOAD_VERSION + ". No-op. "+            "Future kicad_* commands won't re-check; this status is cached for the "+            "bridge process lifetime."+        )+    elif overall_action == "partial-failure":+        result["_hint"] = (+            "AT LEAST ONE KiCad version's plugin install failed. Inspect perVersion[] "+            "for which exact version's errorCode applies. The bridge still works for "+            "the versions that succeeded; the failed ones fall back to legacy UI "+            "automation. Report the specific errorCode to the user — each one has its "+            "own recovery path documented in the per-version `_hint` field."+        )+    elif overall_action == "skipped":+        result["_hint"] = (+            "Plugin install skipped — see per-version errorCodes. Most common reason: "+            "running on macOS/Linux where Phase 2 hasn't been ported yet. Legacy "+            "kicad_* commands keep working; you just don't have the reverse bridge."+        )++    return result+++def ensure_plugin_installed_once(all_kicad_versions: list[dict]) -> dict:+    """Install the plugin if needed; cache the result.++    Idempotent: every call after the first returns the cached dict, marked+    with `triggered: False` and `cached: True` so consumers can tell whether+    this call actually did anything.+    """+    global _install_result  # pylint: disable=global-statement+    with _install_lock:+        if _install_result is not None:+            cached = dict(_install_result)+            cached["triggered"] = False+            cached["cached"] = True+            return cached+        # Honor disable env var. Useful for development/diagnostic.+        if os.environ.get("ADOM_KICAD_PLUGIN_AUTO_INSTALL_DISABLE") == "1":+            _install_result = {+                "triggered": False,+                "summary": "disabled",+                "errorCode": "disabled_via_env",+                "_hint": (+                    "ADOM_KICAD_PLUGIN_AUTO_INSTALL_DISABLE=1 in the kicad bridge's "+                    "environment. To install manually, call: "+                    "adom-desktop kicad_install_plugin (Phase 2.1)."+                ),+            }+            return dict(_install_result)+        _install_result = _do_install(all_kicad_versions)+        return _install_result+++def attach_to_response_if_first(result: dict) -> dict:+    """Attach `pluginAutoInstall` to a response dict EXACTLY ONCE per+    bridge boot. After the AI has been notified on first command, future+    responses skip the field to keep payloads small.++    bridge_status / bridge_call ALWAYS get the field attached regardless,+    since those verbs explicitly ask about plugin state.+    """+    global _response_attached  # pylint: disable=global-statement+    if not isinstance(result, dict):+        return result+    if _response_attached and _install_result is not None:+        return result+    if _install_result is None:+        return result+    result["pluginAutoInstall"] = dict(_install_result)+    _response_attached = True+    return result+++def force_attach(result: dict) -> dict:+    """For bridge_status / bridge_call — always attach the current install+    state, regardless of `_response_attached`. Useful when the AI is+    explicitly inspecting plugin state."""+    if not isinstance(result, dict) or _install_result is None:+        return result+    result["pluginAutoInstall"] = dict(_install_result)+    return result+++def get_cached_result() -> Optional[dict]:+    return dict(_install_result) if _install_result is not None else None+++def force_install(all_kicad_versions: list[dict]) -> dict:+    """Bypass the once-per-boot cache and re-run the install. The new+    result replaces the cache so subsequent attach_to_response_if_first()+    calls see the fresh state.++    Used by the explicit `kicad_install_plugin` verb."""+    global _install_result, _response_attached  # pylint: disable=global-statement+    with _install_lock:+        _install_result = _do_install(all_kicad_versions)+        # Reset the "attached on first response" flag so future calls+        # surface the fresh install state once more.+        _response_attached = False+        return dict(_install_result)+++def handle_install_plugin(kicad_info: dict, args: dict) -> dict:+    """Explicit kicad_install_plugin verb. Bypasses the auto-install+    cache and forces a fresh deploy + status check.++    Args:+      force:  default true. If false, behaves like the auto-install path+              (returns cached result if it's already run this boot).++    Returns the full install report. Useful for: debugging when the+    plugin isn't loading, updating the payload between releases without+    restarting the bridge, recovering from a partial-failure install.+    """+    # Need the full all_kicad_versions list from server.py — caller+    # passed kicad_info (the default version). Re-detect to get all.+    from kicad_detect import detect_all_kicad_versions+    all_versions = detect_all_kicad_versions()++    if args.get("force") is False:+        # Honor explicit force=false by behaving like the auto path+        return {"success": True, "result": ensure_plugin_installed_once(all_versions)}++    result = force_install(all_versions)+    return {+        "success": result.get("summary") not in ("partial-failure",),+        "result": result,+        "_hint": (+            "force-reinstall complete. Future kicad_* commands will show "+            "this fresh install state in their pluginAutoInstall fields. "+            "If KiCad was running during install, ask the user to restart "+            "those processes — the in-memory Python interpreter still has "+            "the OLD plugin loaded until next launch."+        ),+    }
server.pyadded+440
@@ -0,0 +1,440 @@+#!/usr/bin/env python3+"""Adom KiCad Bridge Server — localhost HTTP server for KiCad integration.++Receives commands from the Adom Desktop (Tauri app) and controls+KiCad via kicad-cli, direct executable launch, and library table manipulation.++Usage:+    python server.py+    python server.py --port 8772+"""++import json+import os+import re+import subprocess+import sys+import traceback+from http.server import HTTPServer, BaseHTTPRequestHandler+from pathlib import Path++# Platform detection for v1.7.4+ KiCad-v2 enriched helpers (runningInstances etc.)+IS_WINDOWS = sys.platform == "win32"++# Ensure the plugin root is on the path+sys.path.insert(0, str(Path(__file__).parent))++from kicad_detect import detect_kicad, detect_all_kicad_versions+from capabilities import build_capability_report+from handlers.open_files import handle_open_board, handle_open_schematic+from handlers.open_symbol_editor import handle_open_symbol_editor+from handlers.open_footprint_editor import handle_open_footprint_editor, handle_open_3d_viewer+from handlers.close_windows import (+    handle_close_symbol_editor, handle_close_footprint_editor,+    handle_close_3d_viewer, handle_close_kicad, handle_window_info,+)+from handlers.install_library import handle_install_library+from handlers.install_symbol import handle_install_symbol+from handlers.install_footprint import handle_install_footprint+from handlers.run_drc import handle_run_drc+from handlers.fix_keyboard import handle_fix_keyboard+from handlers.kicad_ui import handle_screenshot_all, handle_send_key, handle_click+from handlers.bridge_client import handle_bridge_status, handle_bridge_call, handle_open_editors+from handlers.kicad_cli_lint import (+    handle_run_erc, handle_lint_board, handle_lint_schematic,+    handle_lint_library, handle_format_upgrade,+)+# v0.7.1+: export handlers (Gerber/PDF/SVG/STEP/BOM_CSV) via kicad-cli. The+# functions have existed in handlers/export.py for a while but were never wired+# into COMMAND_HANDLERS — kicad_export_gerbers/pdf/svg/step/bom_csv all returned+# "Unknown command" at runtime. Discovered while building the KiCad tour skill.+from handlers.export import (+    handle_export_gerber, handle_export_pdf, handle_export_svg,+    handle_export_step, handle_export_bom_csv,+)+from adom_library import ensure_adom_library+import plugin_install++DEFAULT_PORT = 8772+++def _read_bridge_version() -> str:+    """Our own bridge version, from the BRIDGE_VERSION file beside server.py.+    Reported in /health so AD can detect a STALE bridge process (one that+    survived an AD update) and restart it — the "not running latest bridge"+    footgun. Empty string if the file is missing (treated as stale by AD)."""+    try:+        p = os.path.join(os.path.dirname(os.path.abspath(__file__)), "BRIDGE_VERSION")+        with open(p, encoding="utf-8") as fh:+            return fh.read().strip()+    except OSError:+        return ""+++BRIDGE_VERSION = _read_bridge_version()++# Populated at startup+kicad_info = None              # default kicad_info (newest installed)+all_kicad_versions: list = []  # every installed version, newest first++def _handle_adom_library_status(kicad_info: dict, args: dict) -> dict:+    """Return the status of the Adom symbol library."""+    result = ensure_adom_library(kicad_info)+    return {"success": True, **result}+++def _handle_list_versions(kicad_info: dict, args: dict) -> dict:+    """List every installed KiCad version on this machine.++    Returns the full enriched per-version info from kicad_detect.detect_all_*+    plus a `default` flag per entry. v1.7.4+ (KiCad v2 Phase 1): broader+    surface — pythonVersion, pluginDir, adomPlugin{Installed,Version},+    ipcApiAvailable, ipcApiScope, binaries{}, libraryTablePaths{} all+    included per the integration plan. Back-compat: the legacy fields+    (version, base_dir, kicad_exe, default) are all still present.++    Returns: {success, versions:[full info dict + default flag], default,+              activeVersion, count, runningInstances, _hint}+    """+    if not all_kicad_versions:+        return {+            "success": True,+            "versions": [],+            "default": None,+            "activeVersion": None,+            "count": 0,+            "runningInstances": _detect_running_kicad_instances(),+            "_hint": "No KiCad installs found. Install from https://www.kicad.org/download/, or run `adom-desktop desktop_install_kicad '{}'` for an unattended winget install.",+            "errorCode": "kicad_not_installed",+        }+    default_version = all_kicad_versions[0]["version"]+    # Surface every field detect_all_kicad_versions returns + a `default`+    # flag. We deliberately don't hand-pick fields here — callers expecting+    # the v1.7.3 shape (version/base_dir/kicad_exe/default) still get them;+    # callers using the v1.7.4 enriched shape get everything additional.+    versions = []+    for v in all_kicad_versions:+        entry = dict(v)  # shallow copy so we don't mutate the cached info+        entry["default"] = (v["version"] == default_version)+        versions.append(entry)+    return {+        "success": True,+        "versions": versions,+        "default": default_version,             # legacy field — newest version+        "activeVersion": default_version,       # v1.7.4 alias for newest installed+        "count": len(all_kicad_versions),+        "runningInstances": _detect_running_kicad_instances(),+        "_hint": (+            f"Pass kicadVersion to any kicad_open_* command to pick a specific version "+            f"(e.g. {{\"kicadVersion\":\"{versions[-1]['version']}\"}}). "+            f"Omit to use the newest ({default_version}). "+            f"v1.7.4+: each entry now includes pythonVersion, pluginDir, adomPluginInstalled, "+            f"ipcApiAvailable, binaries{{}}, libraryTablePaths{{}} — see the integration plan."+        ),+    }+++def _detect_running_kicad_instances() -> list[dict]:+    """v1.7.4+ (KiCad v2 Phase 1): enumerate currently-running KiCad processes+    via PowerShell. Returns one entry per kicad.exe process with pid + window+    title. Best-effort — empty list on probe failure (PowerShell missing,+    timeout, parse error).++    Doesn't include HWND in v1 — Get-Process doesn't expose it cleanly and the+    Win32 EnumWindows path would need ctypes + window-class matching to+    correlate windows back to pids. Add HWND in a follow-up when a caller+    actually needs it (probably Phase 3's tier-2 WM_COMMAND path).+    """+    if not IS_WINDOWS:+        return []+    try:+        result = subprocess.run(+            [+                "powershell", "-NoProfile", "-NonInteractive", "-Command",+                "Get-Process kicad -ErrorAction SilentlyContinue | "+                "Select-Object Id, MainWindowTitle, Path | "+                "ConvertTo-Json -Compress -AsArray",+            ],+            capture_output=True, text=True, timeout=5,+        )+        if result.returncode != 0 or not result.stdout.strip():+            return []+        import json+        data = json.loads(result.stdout)+        if not isinstance(data, list):+            data = [data]+        out = []+        for proc in data:+            if not isinstance(proc, dict):+                continue+            # Try to infer version from the install path; fall back to None.+            install_path = proc.get("Path") or ""+            version = None+            m = re.search(r"KiCad[\\/](\d+(?:\.\d+)*)[\\/]", install_path)+            if m:+                version = m.group(1)+            out.append({+                "pid": proc.get("Id"),+                "title": proc.get("MainWindowTitle") or "",+                "exePath": install_path or None,+                "version": version,+            })+        return out+    except (subprocess.TimeoutExpired, OSError, ValueError):+        return []+++COMMAND_HANDLERS = {+    "open_board": handle_open_board,+    "open_schematic": handle_open_schematic,+    "open_symbol_editor": handle_open_symbol_editor,+    "open_footprint_editor": handle_open_footprint_editor,+    "open_3d_viewer": handle_open_3d_viewer,+    "close_symbol_editor": handle_close_symbol_editor,+    "close_footprint_editor": handle_close_footprint_editor,+    "close_3d_viewer": handle_close_3d_viewer,+    "close": handle_close_kicad,+    "window_info": handle_window_info,+    "install_library": handle_install_library,+    "install_symbol": handle_install_symbol,+    "install_footprint": handle_install_footprint,+    "run_drc": handle_run_drc,+    "fix_keyboard": handle_fix_keyboard,+    "screenshot_all": handle_screenshot_all,+    "send_key": handle_send_key,+    "click": handle_click,+    "adom_library_status": _handle_adom_library_status,+    "list_versions": _handle_list_versions,+    "bridge_status": handle_bridge_status,+    "bridge_call": handle_bridge_call,+    "open_editors": handle_open_editors,+    "install_plugin": plugin_install.handle_install_plugin,+    # v1.7.12+: headless lint pre-checks via kicad-cli+    "run_erc": handle_run_erc,+    "lint_board": handle_lint_board,+    "lint_schematic": handle_lint_schematic,+    # v1.7.13+: library lint + file-format upgrade+    "lint_library": handle_lint_library,+    "format_upgrade": handle_format_upgrade,+    # v0.7.1+ (BRIDGE_VERSION): export verbs via kicad-cli. Aliases included for+    # the older `gerbers` plural form callers used.+    "export_gerber": handle_export_gerber,+    "export_gerbers": handle_export_gerber,+    "export_pdf": handle_export_pdf,+    "export_svg": handle_export_svg,+    "export_step": handle_export_step,+    "export_bom_csv": handle_export_bom_csv,+}+++def _resolve_kicad_info_for_command(args: dict) -> tuple[dict, str | None]:+    """Pick the kicad_info to pass to a handler based on the optional kicadVersion+    arg. Returns (info, error_dict). If error_dict is None, info is good to use.+    """+    requested = args.get("kicadVersion")+    if not requested:+        return kicad_info, None  # default = newest installed++    # Look up the requested version among all_kicad_versions+    available = [v["version"] for v in all_kicad_versions]+    for v in all_kicad_versions:+        if v["version"] == requested or v["version"].startswith(f"{requested}."):+            return v, None++    return None, {+        "success": False,+        "error": f"KiCad {requested} is not installed. Available: {', '.join(available) or '(none)'}.",+        "errorCode": "kicad_version_not_installed",+        "available_versions": available,+        "default_version": kicad_info.get("version") if kicad_info else None,+        "_hint": (+            f"Pass kicadVersion as one of: {', '.join(available) or '(none)'}, "+            f"or omit it to use the newest ({kicad_info.get('version') if kicad_info else 'none'}). "+            f"Run kicad_list_versions to see installed versions with their paths."+        ),+    }+++def dispatch_command(command: str, args: dict) -> dict:+    handler = COMMAND_HANDLERS.get(command)+    if handler is None:+        return {+            "success": False,+            "error": f"Unknown command: {command}",+            "_hint": f"Valid KiCad commands: {', '.join(sorted(COMMAND_HANDLERS.keys()))}.",+        }++    # Auto-install the Phase 2 reverse-bridge plugin on first call per+    # bridge boot. Idempotent — subsequent calls return the cached result+    # at near-zero cost. The pluginAutoInstall report is attached to the+    # FIRST response so the calling AI sees what happened (or what+    # failed). bridge_status / bridge_call always get the current state+    # attached via plugin_install.force_attach() inside this dispatcher.+    plugin_install.ensure_plugin_installed_once(all_kicad_versions)++    # list_versions doesn't need an installed KiCad — discovery verb.+    # Still gets the pluginAutoInstall attached via the normal once-per-+    # boot path (not force_attach), so the AI's very first kicad_* call+    # gets the install report, but a repeat list_versions doesn't.+    if command == "list_versions":+        result = handler(kicad_info, args)+        plugin_install.attach_to_response_if_first(result)+        return result++    # bridge_status / bridge_call / open_editors / install_plugin are+    # explicit diagnostic verbs that ALWAYS want to see the install+    # state — they exist for the AI to introspect plugin health.+    if command in ("bridge_status", "bridge_call", "open_editors", "install_plugin"):+        result = handler(kicad_info, args)+        plugin_install.force_attach(result)+        return result++    # Pre-check: if KiCad isn't installed at all, return a helpful error with+    # download link instead of the terse "KiCad not installed" from handlers.+    if not kicad_info or not kicad_info.get("installed"):+        return {+            "success": False,+            "error": "KiCad is not installed. "+                     "Download it from https://www.kicad.org/download/ "+                     "(free and open source EDA suite).",+            "errorCode": "kicad_not_installed",+            "_hint": "Report to the user and ask them to install KiCad from https://www.kicad.org/download/, then retry.",+        }++    # Resolve the version to use for this specific call (may differ from default)+    selected_info, err = _resolve_kicad_info_for_command(args)+    if err is not None:+        return err++    result = handler(selected_info, args)+    # Stamp the version actually used so callers can see which install ran+    if isinstance(result, dict):+        result.setdefault("kicadVersionUsed", selected_info.get("version"))+    # Attach plugin auto-install report ONCE per bridge boot (on the+    # first kicad_* command's response) so the AI knows whether the+    # Phase 2 reverse-bridge plugin was deployed / updated / already+    # current. Subsequent commands skip this to keep payloads small.+    plugin_install.attach_to_response_if_first(result)+    return result+++class KiCadBridgeHandler(BaseHTTPRequestHandler):+    """HTTP request handler for the KiCad bridge server."""++    def do_GET(self):+        # /status is the manifest's declared healthEndpoint (bridge.json+        # spawn.healthEndpoint == "/status"). Through v0.8.9 the server only+        # answered /health, so adom-desktop's health probe got a 404, never+        # marked the bridge healthy, and every kicad_* verb blocked the full+        # 30s and timed out (bridgeRunning:false). We now answer BOTH paths+        # with 200 so the bridge actually comes up. /status additionally+        # carries the capability map (KiCad versions + which automations this+        # install supports) so callers can reason about upgrades.+        if self.path in ("/status", "/health"):+            self._respond(200, self._status_payload())+        else:+            self._respond(404, {"error": "Not found"})++    def _status_payload(self) -> dict:+        return {+            "status": "ok",+            "bridgeVersion": BRIDGE_VERSION,+            "kicad": kicad_info,+            "all_versions": [+                {"version": v["version"], "base_dir": v["base_dir"]}+                for v in all_kicad_versions+            ],+            "capabilities": build_capability_report(all_kicad_versions),+        }++    def do_POST(self):+        if self.path != "/command":+            self._respond(404, {"error": "Not found"})+            return++        content_length = int(self.headers.get("Content-Length", 0))+        body = self.rfile.read(content_length)++        try:+            request = json.loads(body)+        except json.JSONDecodeError as e:+            self._respond(400, {"success": False, "error": f"Invalid JSON: {e}"})+            return++        command = request.get("command", "")+        args = request.get("args", {})++        print(f"[KiCad Bridge] Command: {command} | Args: {json.dumps(args)}")++        try:+            result = dispatch_command(command, args)+            self._respond(200, result)+        except Exception as e:+            print(f"[KiCad Bridge] ERROR: {e}")+            traceback.print_exc()+            self._respond(500, {+                "success": False,+                "error": f"Internal error: {e}",+            })++    def _respond(self, status: int, data: dict):+        self.send_response(status)+        self.send_header("Content-Type", "application/json")+        self.end_headers()+        self.wfile.write(json.dumps(data).encode("utf-8"))++    def log_message(self, format, *args):+        print(f"[KiCad Bridge] {args[0]} {args[1]} {args[2]}")+++def main():+    global kicad_info, all_kicad_versions++    port = DEFAULT_PORT+    if "--port" in sys.argv:+        idx = sys.argv.index("--port")+        if idx + 1 < len(sys.argv):+            port = int(sys.argv[idx + 1])++    all_kicad_versions = detect_all_kicad_versions()+    kicad_info = all_kicad_versions[0] if all_kicad_versions else detect_kicad()++    print(f"[KiCad Bridge] KiCad detection result:")+    print(f"  Versions found: {len(all_kicad_versions)}")+    for v in all_kicad_versions:+        marker = " (default)" if v["version"] == kicad_info.get("version") else ""+        print(f"    - {v['version']}{marker} -> {v['base_dir']}")+    if not all_kicad_versions:+        print(f"  WARNING: KiCad not found. Commands will fail until KiCad is installed.")+    else:+        print(f"  Default:   {kicad_info.get('version')}")+        print(f"  Config:    {kicad_info.get('config_dir')}")+        print(f"  User dir:  {kicad_info.get('user_dir')}")+        print(f"  Pass kicadVersion={{\"9.0\"|\"10.0\"|...}} to any open_* command to override.")++    print(f"[KiCad Bridge] Available commands: {', '.join(COMMAND_HANDLERS.keys())}")++    # Ensure the global Adom symbol library exists and is registered+    if kicad_info.get("installed"):+        lib_result = ensure_adom_library(kicad_info)+        if lib_result.get("created"):+            print(f"[KiCad Bridge] Created Adom symbol library (Adom.kicad_sym)")+        if not lib_result.get("registered"):+            print(f"[KiCad Bridge] Registered Adom library in sym-lib-table")+        symbols = lib_result.get("symbols", [])+        print(f"[KiCad Bridge] Adom library: {len(symbols)} symbol(s) — {', '.join(symbols) if symbols else '(empty)'}")++    server = HTTPServer(("127.0.0.1", port), KiCadBridgeHandler)+    print(f"[KiCad Bridge] Listening on http://127.0.0.1:{port}")+    print(f"[KiCad Bridge] Health check: http://127.0.0.1:{port}/health")+    print(f"[KiCad Bridge] Press Ctrl+C to stop.")++    try:+        server.serve_forever()+    except KeyboardInterrupt:+        print("\n[KiCad Bridge] Shutting down.")+        server.server_close()+++if __name__ == "__main__":+    main()
skills/kicad-3d-models/SKILL.mdadded+184
@@ -0,0 +1,184 @@+---+name: kicad-3d-models+description: Add standard KiCad 3D models to a .kicad_pcb file (especially tscircuit exports that ship without model references), open the 3D viewer in KiCad on the user's desktop, and screenshot to verify. Trigger words — 3d models in kicad, kicad 3d viewer, alt+3, no 3d chips, missing 3d models, add 3d models, kicad 3d screenshot, show me 3d in kicad, prove 3d works, kicad models missing.+---++# kicad-3d-models++**Prerequisite: Read `kicad-interaction` skill first.** It covers state checks, window management, and dialog handling that this skill depends on.++Add KiCad-standard 3D model references to `.kicad_pcb` files, push to the user's desktop, open the 3D viewer, and screenshot to prove it worked.++## When to use++- User opens a tscircuit-exported `.kicad_pcb` in KiCad and sees no 3D models (Alt+3 shows bare board)+- User asks to add 3D models to a KiCad PCB+- User wants to see/screenshot the KiCad 3D viewer++## Why tscircuit exports lack 3D models++`tsci export -f kicad_pcb` produces valid PCB layouts (pads, traces, silkscreen, board outline) but does **not** inject `(model ...)` entries into footprint blocks. KiCad's 3D viewer needs those entries to know which `.step`/`.wrl` file to render for each component.++## Step 1 — Discover the KiCad 3D model environment variable++KiCad versions use version-specific env vars. Query `service-kicad` to get the correct one:++```bash+service-kicad fp fetch Resistor_SMD R_0402_1005Metric 2>&1 | grep "model"+```++This returns something like:+```+(model "${KICAD10_3DMODEL_DIR}/Resistor_SMD.3dshapes/R_0402_1005Metric.step"+```++The `${KICAD10_3DMODEL_DIR}` prefix is what you need. Adjust for the KiCad version on the user's desktop.++## Step 2 — Map footprint types to 3D models++List the unique footprint types in the `.kicad_pcb`:++```bash+grep -A1 "(footprint" board.kicad_pcb | grep '"tscircuit:' | sort -u+```++Then build a mapping. Common tscircuit footprint → KiCad 3D model mappings:++| tscircuit footprint | KiCad 3D model path |+|---|---|+| `tscircuit:resistor_0402` | `Resistor_SMD.3dshapes/R_0402_1005Metric.step` |+| `tscircuit:resistor_0603` | `Resistor_SMD.3dshapes/R_0603_1608Metric.step` |+| `tscircuit:resistor_0805` | `Resistor_SMD.3dshapes/R_0805_2012Metric.step` |+| `tscircuit:capacitor_0402` | `Capacitor_SMD.3dshapes/C_0402_1005Metric.step` |+| `tscircuit:capacitor_0603` | `Capacitor_SMD.3dshapes/C_0603_1608Metric.step` |+| `tscircuit:capacitor_0805` | `Capacitor_SMD.3dshapes/C_0805_2012Metric.step` |+| `tscircuit:led_0603_color(...)` | `LED_SMD.3dshapes/LED_0603_1608Metric.step` |+| `tscircuit:TYPE-C-31-M-12` | `Connector_USB.3dshapes/USB_C_Receptacle_HRO_TYPE-C-31-M-12.step` |+| `tscircuit:chip` (machine pins) | Skip or use a small placeholder |++For non-standard footprints, use `service-kicad fp fetch <library> <name>` to look up the model path. Common KiCad 3D model libraries:++- `Resistor_SMD`, `Resistor_THT`+- `Capacitor_SMD`, `Capacitor_THT`+- `LED_SMD`, `LED_THT`+- `Connector_USB`, `Connector_PinHeader`+- `Package_SO`, `Package_QFP`, `Package_BGA`, `Package_DFN_QFN`++## Step 3 — Inject model entries++Write a Python script that:+1. Reads the `.kicad_pcb` file+2. For each `(footprint ...)` block, identifies the footprint type from the line after `(footprint`+3. Finds the closing `)` of the footprint block (by tracking paren depth)+4. Inserts a `(model ...)` block before the closing paren++Model block format:+```+    (model "${KICAD10_3DMODEL_DIR}/Library.3dshapes/ModelName.step"+      (offset (xyz 0 0 0))+      (scale (xyz 1 1 1))+      (rotate (xyz 0 0 0))+    )+```++Verify the result:+```bash+grep "KICAD.*3DMODEL" board.kicad_pcb | sort | uniq -c+```++## Step 4 — Send to desktop and open in KiCad++**CRITICAL: Close all existing KiCad windows BEFORE opening the updated file.** Opening the same file twice creates "File Open Warning" dialogs and leaves stale editors around.++```bash+# Always close first+adom-desktop kicad_close '{}'++# Send files (send PCB and any sidecar STEP/WRL separately if >1MB total to avoid 413)+adom-desktop send_files '{"filePaths":["/path/to/board.kicad_pcb"],"category":"kicad"}'+adom-desktop send_files '{"filePaths":["/path/to/USB_C_model.wrl"],"category":"kicad"}'++# Open fresh+adom-desktop kicad_open_board '{"filePath":"C:/Users/john/Downloads/board.kicad_pcb"}'+```++Wait for the PCB editor to load, then check for dialogs:+```bash+adom-desktop kicad_window_info '{}'+```++## Step 5 — Open the 3D viewer and screenshot++```bash+# Open the 3D viewer (equivalent to Alt+3)+adom-desktop kicad_open_3d_viewer '{"hwnd": <pcb_editor_hwnd>}'++# Wait for rendering, then check the viewer window appeared+sleep 5+adom-desktop kicad_window_info '{}'++# Screenshot the 3D viewer window+adom-desktop desktop_screenshot_window '{"hwnd": <3d_viewer_hwnd>}'+```++The screenshot saves to `/tmp/adom-desktop-screenshots/`. The file is already on Docker — read it with the Read tool to verify the 3D models rendered correctly.++## Handling missing models (e.g. manufacturer-specific USB-C connectors)++Some footprints reference 3D models that aren't in KiCad's standard `packages3D` distribution. Common case: `USB_C_Receptacle_HRO_TYPE-C-31-M-12` ships in the KiCad footprint library but has no matching STEP in packages3D.++**Fix: relative path + ship the STEP alongside the PCB.**++1. Find a visually similar model that IS available:+   ```bash+   service-kicad model fetch "Connector_USB.3dshapes/USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal.step" --out /tmp/usbc-standin.step+   ```+2. Rename it to match the expected filename and place it next to the `.kicad_pcb`+3. Change the model reference in the PCB to a relative path:+   ```+   (model "./USB_C_Receptacle_HRO_TYPE-C-31-M-12.step" ...)+   ```+4. Send BOTH the `.kicad_pcb` and the `.step` file to the desktop (send separately if the STEP triggers a 413 payload-too-large error)++Test with `service-kicad model fetch` first — if it 404s, the model isn't in packages3D and needs this workaround.++**CRITICAL: WRL unit conversion.** KiCad interprets WRL/VRML coordinates as **inches**. If you convert an OBJ/GLB/STL (which are typically in mm) to WRL, you MUST divide all vertex coordinates by 25.4. Without this, the model renders 25.4x too large. STEP files don't have this problem — KiCad reads STEP as mm natively.++## Up-axis rotation convention++STEP files are authored with different up-axis conventions. KiCad uses Z-up. When injecting `(model ...)` references, read `chosen_up_axis` from chip-fetcher's `info.json` to determine the correct rotation:++| `chosen_up_axis` | Source convention | KiCad rotation needed |+|---|---|---|+| `z` | Z-up (KiCad native) | `(rotate (xyz 0 0 0))` |+| `y` | Y-up (Fusion 360, many vendors) | `(rotate (xyz -90 0 0))` |++If `info.json` says `chosen_up_axis: "y"` and you use `(rotate (xyz 0 0 0))`, the model will render sideways or upside-down in KiCad. chip-fetcher's dashboard shows the orientation visually (Z-up/Y-up thumbnail icons) -- check it BEFORE writing the model reference.++See the [`board-building-pipeline`](../board-building-pipeline/SKILL.md) skill for the full up-axis table and how `chosen_up_axis` flows through the entire pipeline (chip-fetcher -> step2glb -> chiplinter -> chipfit -> KiCad footprint writer).++## Footprint source matters (CRITICAL)++When injecting a model reference for a non-standard component, the STEP model and the footprint MUST come from the same source:++- The STEP model from chip-fetcher was designed to match the chip-fetcher footprint (.kicad_mod), **NOT** the tscircuit-exported footprint+- If you source a STEP from SnapMagic/Mouser/manufacturer, you must ALSO use the footprint from that same source+- Replace the entire footprint block in the .kicad_pcb, preserving only the board placement coordinates `(at X Y rot)`++**Why this matters:** A tscircuit footprint and a SnapMagic footprint for the same component (e.g. USB-C HRO TYPE-C-31-M-12) have completely different pad positions and mounting hole locations. Bolting the SnapMagic STEP onto the tscircuit footprint causes pads to be misaligned even though the STEP renders in the 3D viewer -- the model just floats above the wrong pad locations.++**How to replace a footprint block:**+1. Open the chip-fetcher .kicad_mod file for the component+2. Find the matching `(footprint ...)` block in the .kicad_pcb+3. Note the `(at X Y rot)` line from the original block (board placement)+4. Replace the entire footprint block with the chip-fetcher version+5. Update the `(at ...)` line to use the original board placement coordinates+6. Ensure the `(model ...)` reference points to the STEP from the same source++## Checklist before reporting success++- [ ] Model count matches expected component count+- [ ] Screenshot shows 3D component packages (not a bare green board)+- [ ] USB-C connector visible if present+- [ ] LED packages visible and arranged in expected pattern+- [ ] No error dialogs in KiCad
skills/kicad-interaction/SKILL.mdadded+153
@@ -0,0 +1,153 @@+---+name: kicad-interaction+description: Foundational rules for interacting with KiCad on the user's desktop via adom-desktop. Read BEFORE any kicad_open_board, kicad_open_3d_viewer, kicad_send_key, or kicad_screenshot_all call. Covers state checks, window management, dialog handling, file updates, and the 3D viewer workflow. Trigger words — open in kicad, kicad desktop, send to kicad, kicad 3d viewer, kicad screenshot, kicad dialog, kicad file warning, open board in kicad, kicad pcb editor.+---++# kicad-interaction++Read this skill BEFORE any KiCad desktop operation. It prevents the most common mistakes: duplicate windows, unhandled dialogs, and blind file pushes.++## Rule 1 — Always check state first++Before ANY KiCad operation, run:++```bash+adom-desktop kicad_window_info '{}'+```++This returns all open editors, 3D viewers, and modal dialogs with their hwnds. Parse the response and decide your next action based on what's already running.++**If the board is already open and you're pushing an updated file:**+```bash+adom-desktop kicad_close '{}'        # close everything+sleep 2+adom-desktop kicad_open_board '{"filePath":"C:/Users/john/Downloads/board.kicad_pcb"}'+```++**If the board is already open and you just need the 3D viewer:**+```bash+# Reuse the existing editor — get its hwnd from window_info+adom-desktop kicad_open_3d_viewer '{"hwnd": <existing_pcb_editor_hwnd>}'+```++**If KiCad is not running:**+```bash+adom-desktop kicad_open_board '{"filePath":"C:/Users/john/Downloads/board.kicad_pcb"}'+```++Never call `kicad_open_board` without checking `kicad_window_info` first.++## Rule 2 — Never open duplicate instances++Opening the same `.kicad_pcb` file twice triggers a "File Open Warning" dialog about interleaved saves. This is always wrong. The correct pattern when updating a file:++1. `kicad_window_info` — see what's open+2. `kicad_close` — close everything+3. `send_files` — push the updated file+4. `kicad_open_board` — open fresh++Do NOT open a second instance and then dismiss the warning. Close first, open once.++## Rule 3 — Always check for dialogs after operations++After `kicad_open_board`, `kicad_open_3d_viewer`, or any operation that might trigger a dialog:++```bash+sleep 3+adom-desktop kicad_window_info '{}'+```++Check `hasModalDialogs`. If true:+1. Screenshot the dialog: `desktop_screenshot_window '{"hwnd": <dialog_hwnd>}'`+2. Read the screenshot to understand the dialog+3. Dismiss with `kicad_send_key '{"key": "Enter", "hwnd": <dialog_hwnd>}'` or `"Escape"` as appropriate+4. Re-check `kicad_window_info` to confirm it cleared++## Rule 4 — File transfer before open++KiCad runs on Windows. The `.kicad_pcb` file lives on Docker. Always send first, then open:++```bash+# Send files (send large files like STEP/WRL separately to avoid 413)+adom-desktop send_files '{"filePaths":["/docker/path/board.kicad_pcb"],"category":"kicad"}'++# Files land in C:/Users/john/Downloads/ by default+adom-desktop kicad_open_board '{"filePath":"C:/Users/john/Downloads/board.kicad_pcb"}'+```++If you also have sidecar files (STEP/WRL models referenced with relative paths), send those too — they must land in the same directory as the PCB.++## Rule 5 — 3D viewer workflow++```bash+# 1. Check state+adom-desktop kicad_window_info '{}'++# 2. Open 3D viewer from the PCB editor (use hwnd from step 1)+adom-desktop kicad_open_3d_viewer '{"hwnd": <pcb_editor_hwnd>}'++# 3. Wait for render + confirm viewer appeared+sleep 5+adom-desktop kicad_window_info '{}'++# 4. Screenshot the 3D viewer window+adom-desktop desktop_screenshot_window '{"hwnd": <3d_viewer_hwnd>}'++# 5. Read the screenshot to verify models rendered+```++### Edge-mount connectors (USB-C, micro-USB, barrel jacks)++USB-C, micro-USB, barrel jacks, and other connectors that users plug cables into must overhang the board edge. After placing the footprint, verify in the 3D viewer that the connector mouth extends past the Edge.Cuts outline. If it doesn't, adjust the footprint Y position using the fab-layer board-edge alignment line.++**Verification workflow:**+1. Open the 3D viewer after placing an edge-mount connector+2. Screenshot and check that the connector body sticks out past the board edge+3. If it's flush or recessed, the user cannot plug in a cable -- adjust the footprint Y coordinate+4. The footprint's fab layer typically includes a line marking where the board edge should intersect the component++## Rule 6 — Available KiCad commands++```+kicad_open_board          Open a .kicad_pcb in the PCB Editor+kicad_open_3d_viewer      Open 3D viewer from a PCB Editor (needs hwnd)+kicad_close_3d_viewer     Close the 3D viewer+kicad_close               Close all KiCad windows+kicad_window_info         List all open editors, viewers, and dialogs+kicad_send_key            Send a keystroke to a specific window (needs hwnd)+kicad_screenshot_all      Screenshot all KiCad windows+kicad_adom_library_status Check Adom library installation+kicad_install_symbol      Install a symbol to KiCad's library+kicad_install_footprint   Install a footprint to KiCad's library+kicad_install_library     Install a full library+kicad_run_drc             Run Design Rule Check+desktop_screenshot_window Screenshot a specific window by hwnd+desktop_bring_to_front    Focus a window by hwnd+```++## Rule 7 — Pup-over-webview preference for tool dashboards++User prefers tool dashboards (chip-fetcher, chiplinter, etc.) in **pup windows**, not Hydrogen webview tabs. The canonical pattern is two pup windows:++- **Dashboard pup window** (`sessionId: "chip-fetcher-dashboard"` or `"<tool>-dashboard"`) — the live status / card view. Never navigate this mid-batch.+- **Scrape / work pup window** (`sessionId: "chip-fetcher-scrape"` or `"<tool>-scrape"`) — where vendor pages, logins, and downloads happen.++Both windows MUST use the same `profile` (e.g. `profile: "chip-fetcher"`) so cookies and logins persist. Only the `sessionId` varies. This two-pup pattern keeps the user's view of progress (dashboard) completely isolated from the active scraping / interaction window.++## Rule 8 — Desktop connection prerequisite++Before any KiCad command, verify the desktop is connected:++```bash+adom-desktop ping+```++If disconnected, start the relay:+```bash+if ! curl -sf http://127.0.0.1:8766/health >/dev/null 2>&1; then+  nohup adom-desktop serve > /tmp/adom-desktop-relay.log 2>&1 &+  disown+  sleep 3+fi+adom-desktop ping+```
skills/kicad-tour/SKILL.mdadded+199
@@ -0,0 +1,199 @@+---+name: kicad-tour+description: "Run a guided KiCad tour on the user's laptop via adom-desktop — install a custom symbol library, walk the user through Symbol Editor → Footprint Editor → Schematic Editor → PCB Editor → 3D Viewer, then export gerbers. Two prebuilt packs ship under plugins/kicad/tour-pack/ (single resistor, fast) and plugins/kicad/tour-pack-rp2040/ (RP2040 breakout board, comprehensive). Trigger words — kicad tour, walk me through kicad, kicad demo, show me kicad, rp2040 breakout, resistor demo, kicad showcase, end-to-end kicad, send symbol and footprint, kicad export gerbers, gerbers for jlcpcb, gerbers for instapcb, kicad 3d board, kicad design walkthrough."+---++# kicad-tour++A guided 8-step walkthrough of the KiCad bridge end-to-end. Runs from any cloud+Docker session (or HD) — every command dispatches through `adom-desktop kicad_*`+to the bridge on the user's laptop. Each step produces a visible change in+KiCad that a viewer can see (and that the tour-runner script can screenshot).++**Read `kicad-interaction` first.** It covers the state-check + dialog rules+this tour follows.++## When to use++- User asks "show me what KiCad can do" / "walk me through kicad"+- Demo / onboarding flow when first connecting AD to KiCad+- Sanity check after a fresh AD install — proves every bridge verb works++## The 8-step tour (sequential, ~90s end-to-end)++Each step's CLI invocation is shown verbatim. The tour-runner Python script+(`plugins/kicad/skills/kicad-tour/tour_runner.py`) executes these exact calls++ collects screenshots into a single output dir.++### Pre-step — pick a pack++```bash+# Fast demo (single 0805 resistor — proves the verbs work)+PACK=tour-pack+# Comprehensive demo (RP2040 breakout board — proves a real chip works)+PACK=tour-pack-rp2040+```++The packs are mounted by the bridge at:++- `C:/Github/adom-desktop/plugins/kicad/<PACK>/`+- (When AD is installer-deployed, look under+  `%LOCALAPPDATA%/Adom Desktop/bridges-cache/kicad/<PACK>/` — adjust paths.)++### Step 1 — install the custom symbol + footprint libraries++The tour-pack ships a `<Lib>.kicad_sym` and a `<Lib>.pretty/` directory. The+`kicad_install_library` verb copies them to the user's KiCad library tree+(`%USERPROFILE%/Documents/KiCad/10.0/{symbols,footprints}/`) and registers+them in `sym-lib-table` and `fp-lib-table`.++```bash+LIB=AdomTour   # or AdomRP2040 for the RP2040 pack++adom-desktop kicad_install_library "{+  \"libraryPath\":\"C:/Github/adom-desktop/plugins/kicad/${PACK}/${LIB}.kicad_sym\",+  \"libraryType\":\"symbol\",+  \"libraryName\":\"${LIB}\"+}"++adom-desktop kicad_install_library "{+  \"libraryPath\":\"C:/Github/adom-desktop/plugins/kicad/${PACK}/${LIB}.pretty\",+  \"libraryType\":\"footprint\",+  \"libraryName\":\"${LIB}\"+}"+```++### Step 2 — open Symbol Editor on the custom symbol++```bash+SYMBOL=R_AdomTour       # tour-pack+# SYMBOL=RP2040_AdomTour   # tour-pack-rp2040++adom-desktop kicad_open_symbol_editor "{+  \"libraryName\":\"${LIB}\",+  \"symbolName\":\"${SYMBOL}\"+}"+```++Wait ~5s for KiCad's startup. Screenshot via+`kicad_screenshot_all` — should show the new library highlighted in the+left-side library tree.++### Step 3 — open Footprint Editor on the custom footprint++```bash+FOOTPRINT=R_0805_AdomTour       # tour-pack+# FOOTPRINT=QFN-56_AdomRP2040    # tour-pack-rp2040++adom-desktop kicad_open_footprint_editor "{+  \"libraryName\":\"${LIB}\",+  \"footprintName\":\"${FOOTPRINT}\"+}"+```++### Step 4 — show 3D viewer for the footprint++```bash+# editor='fp' explicitly targets the Footprint Editor's 3D viewer+# (KiCad 9 worked via WM_COMMAND 20500; KiCad 10 may need adjustment —+# the fallback to PCB editor below covers the common case.)+adom-desktop kicad_open_3d_viewer '{"editor":"fp"}'+```++If this returns "3D Viewer did not open from Footprint Editor" on KiCad 10,+either skip to step 7 (the board's 3D viewer is the more reliable showcase+anyway) OR send `Alt+3` directly:++```bash+# Need the Footprint Editor's hwnd from kicad_window_info+adom-desktop kicad_send_key "{\"hwnd\":<fp_hwnd>, \"key\":\"alt+3\"}"+```++### Step 5 — open the prebuilt schematic++The tour-pack ships `template/tour-demo.kicad_sch` (single R) or+`template/rp2040-breakout.kicad_sch` (RP2040 breakout). Each is wired to use+the custom library installed in step 1.++```bash+SCH=C:/Github/adom-desktop/plugins/kicad/${PACK}/template/tour-demo.kicad_sch+# SCH=C:/Github/adom-desktop/plugins/kicad/tour-pack-rp2040/template/rp2040-breakout.kicad_sch++adom-desktop kicad_open_schematic "{\"filePath\":\"${SCH}\"}"+```++### Step 6 — open the prebuilt board++Same idea — premade `.kicad_pcb` with the footprint placed and a+`(model "${KICAD10_3DMODEL_DIR}/...")` reference per footprint.++```bash+PCB=C:/Github/adom-desktop/plugins/kicad/${PACK}/template/tour-demo.kicad_pcb+adom-desktop kicad_open_board "{\"filePath\":\"${PCB}\"}"+```++### Step 7 — show the 3D view of the board++```bash+# editor='pcb' to target the board's 3D viewer (and skip the FP path+# which would otherwise be tried first when both editors are open).+adom-desktop kicad_open_3d_viewer '{"editor":"pcb"}'+```++This is the visual climax — the green board appears with the actual+3D chip model rendered on top. For the RP2040 pack this is a QFN-56+package; for the resistor pack it's a tiny 0805 chip.++### Step 8 — export gerbers++```bash+adom-desktop kicad_export_gerbers "{+  \"filePath\":\"${PCB}\",+  \"outputDir\":\"C:/tmp/${PACK}-gerbers\"+}"+```++Returns the list of generated `.gbr` / `.gtl` / `.gbl` / `.gm1` files.+These are JLCPCB- and InstaPCB-ready directly.++## Failure modes + recovery++| Symptom | Cause | Recovery |+|---|---|---|+| `Unknown command: export_gerbers` | Bridge < v0.7.1 | Update via `bridge_reinstall '{"name":"kicad"}'` |+| `3D Viewer did not open from Footprint Editor` | KiCad 10 WM_COMMAND ID different | Pass `editor='pcb'` (step 7 path) — works on board |+| `Library 'AdomTour' not found` after opening editor | KiCad cached its old lib-table state | Close all KiCad windows + reopen — sym/fp-lib-table is read at startup |+| Schematic opens with "older format" warning bar | Tour-pack files are KiCad 9 sexpr | Tolerable; KiCad 10 reads them. They convert on save. |+| `Cannot find footprint AdomTour:R_0805_AdomTour` | Footprint library install failed | Re-run step 1b; verify `%USERPROFILE%/Documents/KiCad/10.0/footprints/AdomTour.pretty/` exists |++## Cleanup++```bash+adom-desktop kicad_close '{}'                       # close all KiCad windows+rm -rf "%USERPROFILE%/Documents/KiCad/10.0/symbols/AdomTour.kicad_sym"+rm -rf "%USERPROFILE%/Documents/KiCad/10.0/footprints/AdomTour.pretty"+# (Or leave them — they're harmless even when not actively tour-touring.)+```++## Tour-runner Python++`plugins/kicad/skills/kicad-tour/tour_runner.py` orchestrates all 8 steps+non-interactively + pulls every screenshot to a local output dir. Usage:++```bash+python plugins/kicad/skills/kicad-tour/tour_runner.py \+    --pack tour-pack \+    --output /tmp/kicad-tour-shots+# or+python plugins/kicad/skills/kicad-tour/tour_runner.py \+    --pack tour-pack-rp2040 \+    --output /tmp/rp2040-tour-shots+```++Each step's CLI call is shown above stdout in human-readable form, with+the resulting screenshot named `01-symbol-editor.png`, `02-footprint-editor.png`,+etc. Exits 0 on full success.++When run from inside a Docker container (= relay path) vs. on the host+(= direct API path) the script's `adom-desktop` subprocess transparently+picks whichever is reachable. No config required.
skills/kicad-tour/hero/README.mdadded+67
@@ -0,0 +1,67 @@+# kicad-tour hero collage++A single PNG that explains the kicad-bridge tour in one image. Used as the+wiki page header for `apps/adom-desktop-kicad-bridge`.++## Files++| Path | What |+|---|---|+| `hero.png` | The rendered 1568×980 collage |+| `hero_builder.py` | The Python build script (re-runnable) |+| `tiles/01-symbol.png` .. `06-3d-board.png` | The 6 source KiCad window captures |+| `fonts/FamiljenGrotesk-{Bold,Medium,Regular}.ttf` | Adom headline font (OFL) |+| `fonts/Satoshi-{Regular,Medium}.ttf` | Adom body font (Fontshare free license) |++All paths in `hero_builder.py` are resolved relative to the script's own+location — re-rendering needs no environment setup beyond Pillow.++## Re-render++```bash+cd plugins/kicad/skills/kicad-tour/hero+python hero_builder.py+# wrote hero.png (~330 KB, 1568x980)+```++Requires Python 3.10+ with Pillow.++## Re-capture the tile screenshots++The tiles are captured from running `tour_runner.py --pack tour-pack-rp2040`+in the parent skill directory, then renaming the output frames:++```bash+cd plugins/kicad/skills/kicad-tour+python tour_runner.py --pack tour-pack-rp2040 --output /tmp/rp-shots++# Rename + copy to hero/tiles/+cp /tmp/rp-shots/02-symbol-editor.png    hero/tiles/01-symbol.png+cp /tmp/rp-shots/03-footprint-editor.png hero/tiles/02-footprint.png+cp /tmp/rp-shots/07-board-3d.png         hero/tiles/03-3d-chip.png   # cropped on chip+cp /tmp/rp-shots/05-schematic.png        hero/tiles/04-schematic.png+cp /tmp/rp-shots/06-board.png            hero/tiles/05-board.png+cp /tmp/rp-shots/07-board-3d.png         hero/tiles/06-3d-board.png+```++Note: tile 03 ("3D Chip") is the same source as tile 06 — `hero_builder.py`+center-crops it tight on the chip via the `crop_chip` flag. If you ever+capture a real footprint-editor 3D view (the v0.8.5 bridge tries via+`kicad_open_3d_viewer editor=fp` but KiCad 10's WM_COMMAND ID isn't+matched yet), drop that capture in as `03-3d-chip.png` and remove the+`"crop_chip"` flag in `hero_builder.py:TILES_DEF`.++## Brand tokens++The teal / dark teal / muted greys are pulled from+`gallia/brand/logos/SKILL.md`. If the brand palette shifts, swap the+constants at the top of `hero_builder.py` — they're all RGB tuples.++## Font licenses++- **Familjen Grotesk** — SIL Open Font License v1.1 (Martin Sommaruga & Iza Bargmann). Free to ship in any context.+- **Satoshi** — Fontshare free license (Indian Type Foundry). Free for personal + commercial use, including in apps and websites; no attribution required.++Both TTFs were converted on-host from the gallia woff2 sources via `fontTools` ++`brotli` (one-time). The source woff2 lives at `gallia/brand/fonts/` if you+need other weights.
skills/kicad-tour/hero/hero_builder.pyadded+237
@@ -0,0 +1,237 @@+#!/usr/bin/env python3+"""Build the kicad-tour hero collage from the 6 tile screenshots.++All paths are resolved relative to this script. To regenerate:++    cd plugins/kicad/skills/kicad-tour/hero+    python hero_builder.py++Inputs:+- tiles/01-symbol.png .. 06-3d-board.png — 6 captured KiCad screenshots+- fonts/{FamiljenGrotesk-Bold,Medium,Regular,Satoshi-Regular,Medium}.ttf++Output:+- hero.png (1568×980)++Adom brand tokens are inlined at the top — swap colors there if the brand+shifts. Layout knobs (tile gap, padding, header heights) are also at the top.++The tiles are real KiCad window captures from the RP2040 breakout tour,+not mockups. To recapture: run the tour_runner.py script in the parent+skill dir against the tour-pack-rp2040 pack, then copy the resulting+screenshots into tiles/ with the names above.+"""+from __future__ import annotations+from pathlib import Path+from PIL import Image, ImageDraw, ImageFont++# ── Resolve relative paths from this script's location ─────────────────+HERE = Path(__file__).resolve().parent+TILES = HERE / "tiles"+FONT_DIR = HERE / "fonts"+OUT = HERE / "hero.png"++# ── Brand tokens (Adom) ────────────────────────────────────────────────+TEAL = (0, 184, 176)+TEAL_DARK = (0, 61, 64)+TEAL_DEEP = (0, 33, 36)+WHITE = (255, 255, 255)+MUTED = (174, 184, 188)+BG_TOP = (8, 22, 24)+BG_BOT = (3, 12, 14)++# ── Layout ─────────────────────────────────────────────────────────────+W, H = 1568, 980+PAD = 32++# ── Fonts ──────────────────────────────────────────────────────────────+FG_BOLD = FONT_DIR / "FamiljenGrotesk-Bold.ttf"+FG_MEDIUM = FONT_DIR / "FamiljenGrotesk-Medium.ttf"+FG_REGULAR = FONT_DIR / "FamiljenGrotesk-Regular.ttf"+SAT_REGULAR = FONT_DIR / "Satoshi-Regular.ttf"+SAT_MEDIUM = FONT_DIR / "Satoshi-Medium.ttf"+++def font(path: Path, size: int) -> ImageFont.FreeTypeFont:+    return ImageFont.truetype(str(path), size)+++# ── Tile manifest ──────────────────────────────────────────────────────+# (filename, label, sublabel, optional flag)+TILES_DEF = [+    ("01-symbol.png", "Symbol", "kicad_install_library + kicad_open_symbol_editor", None),+    ("02-footprint.png", "Footprint", "Install + open the QFN-56 footprint", None),+    ("03-3d-chip.png", "3D Chip", "kicad_open_3d_viewer — model loads from .step", "crop_chip"),+    ("04-schematic.png", "Schematic", "kicad_open_schematic — RP2040 placed on the sheet", None),+    ("05-board.png", "2D Board Layout", "kicad_open_board — footprint on the outline", None),+    ("06-3d-board.png", "3D Board", "editor='pcb' renders the chip on the board", None),+]+++def vertical_gradient(top: tuple, bottom: tuple, w: int, h: int) -> Image.Image:+    img = Image.new("RGB", (w, h), top)+    d = ImageDraw.Draw(img)+    for y in range(h):+        f = y / max(1, h - 1)+        c = (+            int(top[0] + (bottom[0] - top[0]) * f),+            int(top[1] + (bottom[1] - top[1]) * f),+            int(top[2] + (bottom[2] - top[2]) * f),+        )+        d.line([(0, y), (w, y)], fill=c)+    return img+++def rounded_panel(size: tuple, radius: int, fill: tuple,+                  stroke: tuple | None = None, stroke_w: int = 1) -> Image.Image:+    w, h = size+    img = Image.new("RGBA", (w, h), (0, 0, 0, 0))+    d = ImageDraw.Draw(img)+    d.rounded_rectangle((0, 0, w - 1, h - 1), radius=radius, fill=fill)+    if stroke is not None:+        d.rounded_rectangle((0, 0, w - 1, h - 1), radius=radius, outline=stroke, width=stroke_w)+    return img+++def fit_into(img: Image.Image, target_w: int, target_h: int,+             *, crop_chip: bool = False) -> Image.Image:+    """Letterbox-fit `img` into (target_w × target_h)."""+    if crop_chip:+        # Crop tight on the QFN-56 chip in the centre of the 3D board view.+        # The v2 breakout board is 60x25 mm so the chip is small relative to+        # the canvas — tighten the crop window vs the v1 single-resistor pack.+        sw, sh = img.size+        cw, ch = int(sw * 0.26), int(sh * 0.38)+        cx, cy = sw // 2, int(sh * 0.5)+        left = max(0, cx - cw // 2)+        top = max(0, cy - ch // 2)+        img = img.crop((left, top, left + cw, top + ch))++    sw, sh = img.size+    src_ratio = sw / sh+    dst_ratio = target_w / target_h+    if src_ratio > dst_ratio:+        new_w = target_w+        new_h = int(sh * (new_w / sw))+    else:+        new_h = target_h+        new_w = int(sw * (new_h / sh))+    resized = img.resize((new_w, new_h), Image.LANCZOS)+    canvas = Image.new("RGB", (target_w, target_h), TEAL_DEEP)+    canvas.paste(resized, ((target_w - new_w) // 2, (target_h - new_h) // 2))+    return canvas+++def draw_tile(canvas: Image.Image, x: int, y: int, w: int, h: int,+              src_path: Path, label: str, sublabel: str, index: int,+              *, crop_chip: bool = False) -> None:+    # Card background+    card = rounded_panel((w, h), 18, fill=(*TEAL_DEEP, 220), stroke=(50, 100, 102), stroke_w=1)+    canvas.paste(card, (x, y), card)++    img_h = int(h * 0.72)+    inner_pad = 12+    img_box = (x + inner_pad, y + inner_pad, x + w - inner_pad, y + inner_pad + img_h)++    src = Image.open(src_path).convert("RGB")+    fitted = fit_into(src, img_box[2] - img_box[0], img_box[3] - img_box[1],+                      crop_chip=crop_chip)+    mask = Image.new("L", fitted.size, 0)+    ImageDraw.Draw(mask).rounded_rectangle(+        (0, 0, fitted.size[0], fitted.size[1]), radius=10, fill=255+    )+    fitted_rgba = fitted.convert("RGBA")+    fitted_rgba.putalpha(mask)+    canvas.paste(fitted_rgba, (img_box[0], img_box[1]), fitted_rgba)++    # Number chip + label + sublabel+    label_y = img_box[3] + 14+    d = ImageDraw.Draw(canvas)+    num_text = f"0{index+1}" if (index + 1) < 10 else str(index + 1)+    num_w, num_h = 36, 20+    num_x = x + inner_pad+    num_y = label_y+    d.rounded_rectangle((num_x, num_y, num_x + num_w, num_y + num_h), radius=10, fill=TEAL)+    num_font = font(FG_BOLD, 12)+    bbox = d.textbbox((0, 0), num_text, font=num_font)+    tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]+    d.text((num_x + (num_w - tw) // 2, num_y + (num_h - th) // 2 - 1),+           num_text, fill=TEAL_DEEP, font=num_font)+    d.text((num_x + num_w + 10, num_y - 2), label, fill=WHITE, font=font(FG_BOLD, 20))+    d.text((num_x, num_y + num_h + 10), sublabel, fill=MUTED, font=font(SAT_REGULAR, 12))+++def main() -> int:+    base = vertical_gradient(BG_TOP, BG_BOT, W, H).convert("RGBA")+    d = ImageDraw.Draw(base)++    # ── Header ─────────────────────────────────────────────────────────+    badge_x, badge_y = PAD, PAD+    badge_w, badge_h = 70, 26+    d.rounded_rectangle((badge_x, badge_y, badge_x + badge_w, badge_y + badge_h),+                        radius=13, fill=TEAL)+    bf = font(FG_BOLD, 14)+    bb = d.textbbox((0, 0), "ADOM", font=bf)+    bw, bh = bb[2] - bb[0], bb[3] - bb[1]+    d.text((badge_x + (badge_w - bw) // 2, badge_y + (badge_h - bh) // 2 - 1),+           "ADOM", fill=TEAL_DEEP, font=bf)+    d.text((badge_x + badge_w + 14, badge_y + 4), "DESKTOP  ·  KICAD BRIDGE",+           fill=MUTED, font=font(SAT_MEDIUM, 13))++    headline = "Drive KiCad from anywhere."+    d.text((PAD, badge_y + badge_h + 14), headline,+           fill=WHITE, font=font(FG_BOLD, 56))++    sub_a = "Send a custom symbol, footprint and 3D model into KiCad on the user's laptop."+    sub_b = "Open the schematic. Place the board. Render in 3D. Export gerbers to JLCPCB / InstaPCB."+    d.text((PAD, badge_y + badge_h + 14 + 64), sub_a,+           fill=(220, 230, 232), font=font(SAT_REGULAR, 19))+    d.text((PAD, badge_y + badge_h + 14 + 64 + 28), sub_b,+           fill=(180, 195, 198), font=font(SAT_REGULAR, 19))++    chip_text = "8 verbs · 90 seconds · 1 chip"+    cf = font(FG_BOLD, 14)+    cb = d.textbbox((0, 0), chip_text, font=cf)+    cw, ch = cb[2] - cb[0], cb[3] - cb[1]+    chip_pad_x, chip_pad_y = 18, 11+    chip_x = W - PAD - cw - 2 * chip_pad_x+    chip_y = PAD + 4+    d.rounded_rectangle(+        (chip_x, chip_y, chip_x + cw + 2 * chip_pad_x, chip_y + ch + 2 * chip_pad_y),+        radius=16, fill=(*TEAL_DARK, 255), outline=TEAL, width=1,+    )+    d.text((chip_x + chip_pad_x, chip_y + chip_pad_y - 1), chip_text,+           fill=TEAL, font=cf)++    # ── Tiles 2×3 grid ────────────────────────────────────────────────+    grid_top = badge_y + badge_h + 14 + 64 + 28 + 38+    gap = 22+    tile_w = (W - 2 * PAD - 2 * gap) // 3+    tile_h = (H - grid_top - PAD - gap - 80) // 2++    for i, (fname, label, sublabel, flag) in enumerate(TILES_DEF):+        col, row = i % 3, i // 3+        x = PAD + col * (tile_w + gap)+        y = grid_top + row * (tile_h + gap)+        draw_tile(base, x, y, tile_w, tile_h,+                  src_path=TILES / fname,+                  label=label, sublabel=sublabel, index=i,+                  crop_chip=(flag == "crop_chip"))++    # ── Footer ────────────────────────────────────────────────────────+    foot_y = grid_top + 2 * tile_h + gap + 18+    flow = "send symbol  →  send footprint  →  3D-verify chip  →  schematic  →  layout  →  3D board  →  gerbers"+    d.text((PAD, foot_y), flow, fill=(190, 205, 208), font=font(SAT_MEDIUM, 16))+    cta = "adom-desktop  kicad_*  ·  v1.8.97  ·  Bridge v0.8.5"+    cf2 = font(FG_BOLD, 14)+    cb2 = d.textbbox((0, 0), cta, font=cf2)+    cw2 = cb2[2] - cb2[0]+    d.text((W - PAD - cw2, foot_y), cta, fill=TEAL, font=cf2)++    base.convert("RGB").save(OUT, optimize=True)+    print(f"wrote {OUT} ({OUT.stat().st_size:,} bytes, {W}x{H})")+    return 0+++if __name__ == "__main__":+    raise SystemExit(main())
skills/kicad-tour/tour_runner.pyadded+271
@@ -0,0 +1,271 @@+#!/usr/bin/env python3+"""KiCad tour runner — executes the 8-step tour via `adom-desktop kicad_*`+CLI calls and writes each step's screenshot to an output dir.++Usage:+    python tour_runner.py --pack tour-pack          --output /tmp/tour+    python tour_runner.py --pack tour-pack-rp2040   --output /tmp/rp2040-tour++The script is deliberately a thin orchestrator: every step is a single+CLI invocation, and every wait is a fixed `sleep`. Caller can read the+script as the canonical sequence of bridge calls; the same calls work+from a `bash` tour or any other orchestrator.+"""++from __future__ import annotations+import argparse+import json+import os+import shutil+import subprocess+import sys+import time+from pathlib import Path+from typing import Any++# Per-pack config — each pack has its own symbol/footprint/template names+PACK_CONFIG = {+    "tour-pack": {+        "library": "AdomTour",+        "symbol": "R_AdomTour",+        "footprint": "R_0805_AdomTour",+        "sch_filename": "tour-demo.kicad_sch",+        "pcb_filename": "tour-demo.kicad_pcb",+    },+    "tour-pack-rp2040": {+        "library": "AdomRP2040",+        "symbol": "RP2040_AdomTour",+        "footprint": "QFN-56_AdomRP2040",+        "sch_filename": "rp2040-breakout.kicad_sch",+        "pcb_filename": "rp2040-breakout.kicad_pcb",+    },+}+++def adom(verb: str, args: dict | None = None) -> dict[str, Any]:+    """Run `adom-desktop <verb> '<json>'` and return the parsed response."""+    cmd = ["adom-desktop", verb]+    if args is not None:+        cmd.append(json.dumps(args))+    print(f"  $ adom-desktop {verb} {json.dumps(args) if args else ''}")+    proc = subprocess.run(cmd, capture_output=True, text=True, timeout=180)+    if proc.returncode != 0 and not proc.stdout.strip():+        print(f"  ! exit {proc.returncode}: {proc.stderr.strip()[:200]}")+    try:+        return json.loads(proc.stdout)+    except json.JSONDecodeError:+        return {"success": False, "error": f"non-JSON stdout: {proc.stdout[:300]}"}+++def pull_screenshot(hwnd: int, dest: Path, label: str) -> bool:+    """Capture a window via desktop_screenshot_window + adom-desktop pull_file."""+    resp = adom("desktop_screenshot_window", {"hwnd": hwnd})+    full = resp.get("fullPathHost") or (resp.get("data") or {}).get("fullPathHost")+    if not full:+        print(f"  ! {label}: no fullPathHost in response")+        return False+    # Try the safe (downscaled) variant if the full one is unwieldy+    safe = resp.get("safePathHost") or (resp.get("data") or {}).get("safePathHost")+    src = safe or full+    pull = adom("pull_file", {"filePaths": [src], "saveTo": str(dest.parent)})+    # adom-desktop's pull_file lands files in saveTo with their basename. Rename.+    landed = dest.parent / Path(src).name+    if landed.exists():+        landed.rename(dest)+        print(f"  ✓ {label} → {dest.name}")+        return True+    # Fall back to copying via WSL bash if pull_file didn't land (relay vs direct)+    if Path(src.replace("C:", "/c").replace("\\", "/")).exists():+        shutil.copy(src.replace("C:", "/c").replace("\\", "/"), dest)+        print(f"  ✓ {label} → {dest.name} (fallback copy)")+        return True+    print(f"  ! {label}: pull_file did not land the file ({src})")+    return False+++def find_window(title_substring: str) -> int | None:+    """Find a KiCad window whose title contains `title_substring`."""+    resp = adom("kicad_window_info")+    editors = (resp.get("data") or {}).get("editors") or []+    for w in editors:+        if title_substring in (w.get("title") or ""):+            return int(w["hwnd"])+    return None+++def main() -> int:+    parser = argparse.ArgumentParser(description="KiCad tour runner.")+    parser.add_argument("--pack", required=True, choices=list(PACK_CONFIG.keys()))+    parser.add_argument(+        "--pack-dir",+        default=None,+        help="Override the pack root path. Default uses the bundled location.",+    )+    parser.add_argument("--output", required=True, help="Output dir for screenshots.")+    parser.add_argument(+        "--no-cleanup",+        action="store_true",+        help="Don't call kicad_close at the end.",+    )+    parser.add_argument(+        "--skip-fp-3d",+        action="store_true",+        help="Skip step 4 (FP-editor 3D viewer) — KiCad 10 doesn't always respond to the WM_COMMAND.",+    )+    args = parser.parse_args()++    cfg = PACK_CONFIG[args.pack]+    pack_dir = Path(args.pack_dir) if args.pack_dir else Path(+        f"C:/Github/adom-desktop/plugins/kicad/{args.pack}"+    )+    out_dir = Path(args.output)+    out_dir.mkdir(parents=True, exist_ok=True)++    sym_lib = pack_dir / f"{cfg['library']}.kicad_sym"+    fp_lib = pack_dir / f"{cfg['library']}.pretty"+    sch_path = pack_dir / "template" / cfg["sch_filename"]+    pcb_path = pack_dir / "template" / cfg["pcb_filename"]++    print(f"\n=== KiCad Tour: {args.pack} ===")+    print(f"  library  : {cfg['library']}")+    print(f"  symbol   : {cfg['symbol']}")+    print(f"  footprint: {cfg['footprint']}")+    print(f"  output   : {out_dir}\n")++    fails: list[str] = []++    def step(n: int, name: str) -> None:+        print(f"\n--- Step {n}: {name} ---")++    # Step 1a — install symbol library+    step(1, "install symbol library")+    resp = adom("kicad_install_library", {+        "libraryPath": str(sym_lib).replace("\\", "/"),+        "libraryType": "symbol",+        "libraryName": cfg["library"],+    })+    if not resp.get("success"):+        fails.append(f"1a: {resp.get('error')}")++    # Step 1b — install footprint library+    step(1, "install footprint library")+    resp = adom("kicad_install_library", {+        "libraryPath": str(fp_lib).replace("\\", "/"),+        "libraryType": "footprint",+        "libraryName": cfg["library"],+    })+    if not resp.get("success"):+        fails.append(f"1b: {resp.get('error')}")++    # Step 2 — open Symbol Editor+    step(2, f"open Symbol Editor on {cfg['symbol']}")+    resp = adom("kicad_open_symbol_editor", {+        "libraryName": cfg["library"],+        "symbolName": cfg["symbol"],+    })+    if not resp.get("success"):+        fails.append(f"2: {resp.get('error')}")+    time.sleep(6)+    sym_hwnd = find_window("Symbol Editor")+    if sym_hwnd:+        pull_screenshot(sym_hwnd, out_dir / "02-symbol-editor.png", "Symbol Editor")+    else:+        fails.append("2: Symbol Editor window not found")++    # Step 3 — open Footprint Editor+    step(3, f"open Footprint Editor on {cfg['footprint']}")+    resp = adom("kicad_open_footprint_editor", {+        "libraryName": cfg["library"],+        "footprintName": cfg["footprint"],+    })+    if not resp.get("success"):+        fails.append(f"3: {resp.get('error')}")+    time.sleep(6)+    fp_hwnd = find_window("Footprint Editor")+    if fp_hwnd:+        pull_screenshot(fp_hwnd, out_dir / "03-footprint-editor.png", "Footprint Editor")+    else:+        fails.append("3: Footprint Editor window not found")++    # Step 4 — show 3D viewer for footprint (optional)+    if not args.skip_fp_3d:+        step(4, "FP-editor 3D viewer (Alt+3)")+        resp = adom("kicad_open_3d_viewer", {"editor": "fp"})+        if resp.get("success"):+            time.sleep(4)+            v_hwnd = find_window("3D Viewer")+            if v_hwnd:+                pull_screenshot(v_hwnd, out_dir / "04-footprint-3d.png", "FP 3D Viewer")+                adom("kicad_close_3d_viewer")+                time.sleep(2)+            else:+                print("  ⚠ FP 3D viewer reported success but window not found.")+        else:+            print(f"  ⚠ FP 3D viewer failed (known KiCad 10 issue): {resp.get('error')}")++    # Close FP editor so step 7's 3D viewer routes to PCB+    adom("kicad_close_footprint_editor")+    time.sleep(2)++    # Step 5 — open schematic+    step(5, "open schematic")+    resp = adom("kicad_open_schematic", {"filePath": str(sch_path).replace("\\", "/")})+    if not resp.get("success"):+        fails.append(f"5: {resp.get('error')}")+    time.sleep(8)+    sch_hwnd = find_window("Schematic Editor")+    if sch_hwnd:+        pull_screenshot(sch_hwnd, out_dir / "05-schematic.png", "Schematic Editor")+    else:+        fails.append("5: Schematic Editor window not found")++    # Step 6 — open board+    step(6, "open board")+    resp = adom("kicad_open_board", {"filePath": str(pcb_path).replace("\\", "/")})+    if not resp.get("success"):+        fails.append(f"6: {resp.get('error')}")+    time.sleep(8)+    pcb_hwnd = find_window("PCB Editor")+    if pcb_hwnd:+        pull_screenshot(pcb_hwnd, out_dir / "06-board.png", "PCB Editor")+    else:+        fails.append("6: PCB Editor window not found")++    # Step 7 — show 3D viewer for board (always works; uses editor='pcb' to+    # skip the FP path)+    step(7, "PCB 3D viewer")+    resp = adom("kicad_open_3d_viewer", {"editor": "pcb"})+    if not resp.get("success"):+        fails.append(f"7: {resp.get('error')}")+    time.sleep(6)+    v_hwnd = find_window("3D Viewer")+    if v_hwnd:+        pull_screenshot(v_hwnd, out_dir / "07-board-3d.png", "Board 3D Viewer")+    else:+        fails.append("7: 3D Viewer window not found")++    # Step 8 — export gerbers+    step(8, "export gerbers")+    gerber_dir = str(out_dir / "gerbers").replace("\\", "/")+    resp = adom("kicad_export_gerbers", {+        "filePath": str(pcb_path).replace("\\", "/"),+        "outputDir": gerber_dir,+    })+    if not resp.get("success"):+        fails.append(f"8: {resp.get('error')}")+    files = (resp.get("data") or {}).get("files") or []+    print(f"  ✓ wrote {len(files)} gerber files to {gerber_dir}")++    # Cleanup+    if not args.no_cleanup:+        print("\n--- Cleanup: close KiCad ---")+        adom("kicad_close")++    print(f"\n=== Done — {len(fails)} failures ===")+    for f in fails:+        print(f"  • {f}")+    return 1 if fails else 0+++if __name__ == "__main__":+    sys.exit(main())
start.batadded+34
@@ -0,0 +1,34 @@+@echo off+REM Adom KiCad Bridge Server - Startup Script+REM Tries system Python first, falls back to KiCad's bundled Python.++echo [Adom KiCad Bridge] Starting server...++REM Try system Python+where python >nul 2>&1+if %ERRORLEVEL% equ 0 (+    echo [Adom KiCad Bridge] Using system Python+    python "%~dp0server.py" %*+    goto :end+)++REM Try KiCad 9.0 bundled Python+set KICAD_PYTHON=C:\Program Files\KiCad\9.0\bin\python.exe+if exist "%KICAD_PYTHON%" (+    echo [Adom KiCad Bridge] Using KiCad 9.0 bundled Python+    "%KICAD_PYTHON%" "%~dp0server.py" %*+    goto :end+)++REM Try KiCad 8.0 bundled Python+set KICAD_PYTHON=C:\Program Files\KiCad\8.0\bin\python.exe+if exist "%KICAD_PYTHON%" (+    echo [Adom KiCad Bridge] Using KiCad 8.0 bundled Python+    "%KICAD_PYTHON%" "%~dp0server.py" %*+    goto :end+)++echo [Adom KiCad Bridge] ERROR: Python not found. Install Python 3.10+ or KiCad 9.0.+pause++:end
start.shadded+46
@@ -0,0 +1,46 @@+#!/bin/sh+# Adom KiCad Bridge Server — Startup Script (macOS / Linux)+# Tries system Python first, then KiCad's bundled Python on macOS.++set -e++DIR="$(cd "$(dirname "$0")" && pwd)"++echo "[Adom KiCad Bridge] Starting server..."++# 1. Prefer python3.13 / 3.12 / 3.11 / 3.10 in PATH (the type-hint syntax in+#    handlers requires 3.10+). Fall back to system python3 (may be 3.9 on+#    older macOS — that path is fine if the handlers only use older syntax,+#    but warn).+for cand in python3.13 python3.12 python3.11 python3.10; do+    if command -v "$cand" >/dev/null 2>&1; then+        echo "[Adom KiCad Bridge] Using $cand"+        exec "$cand" "$DIR/server.py" "$@"+    fi+done++# 2. KiCad 9 bundled Python on macOS+KICAD_MAC_PY="/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/Current/bin/python3"+if [ -x "$KICAD_MAC_PY" ]; then+    echo "[Adom KiCad Bridge] Using KiCad 9 bundled Python"+    exec "$KICAD_MAC_PY" "$DIR/server.py" "$@"+fi++# 3. System python3 (last resort — may be 3.9 on stock macOS, which+#    will fail at handler-import time on the type-hint syntax).+if command -v python3 >/dev/null 2>&1; then+    PY_VER=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')+    case "$PY_VER" in+        3.10|3.11|3.12|3.13|3.14)+            echo "[Adom KiCad Bridge] Using system python3 ($PY_VER)"+            exec python3 "$DIR/server.py" "$@"+            ;;+        *)+            echo "[Adom KiCad Bridge] WARNING: python3 is $PY_VER (need >= 3.10). Trying anyway..."+            exec python3 "$DIR/server.py" "$@"+            ;;+    esac+fi++echo "[Adom KiCad Bridge] ERROR: No Python 3.10+ found. Install via 'brew install [email protected]' or KiCad 9+." >&2+exit 1
tour-pack-rp2040/AdomRP2040.3dshapes/MachinePin_Medium_Short.stepadded+3146
@@ -0,0 +1,3146 @@+ISO-10303-21;+HEADER;+/* Generated by software containing ST-Developer+ * from STEP Tools, Inc. (www.steptools.com) + */++FILE_DESCRIPTION(+/* description */ (''),+/* implementation_level */ '2;1');++FILE_NAME(+/* name */ 'MachinePin_Medium_Short v28.step',+/* time_stamp */ '2025-05-14T14:56:36-04:00',+/* author */ (''),+/* organization */ (''),+/* preprocessor_version */ 'ST-DEVELOPER v20.1',+/* originating_system */ 'Autodesk Translation Framework v14.4.0.0',+/* authorisation */ '');++FILE_SCHEMA (('AUTOMOTIVE_DESIGN { 1 0 10303 214 3 1 1 }'));+ENDSEC;++DATA;+#10=MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(#25,#26),+#2253);+#11=ITEM_DEFINED_TRANSFORMATION($,$,#941,#1054);+#12=(+REPRESENTATION_RELATIONSHIP($,$,#2264,#2263)+REPRESENTATION_RELATIONSHIP_WITH_TRANSFORMATION(#11)+SHAPE_REPRESENTATION_RELATIONSHIP()+);+#13=CONTEXT_DEPENDENT_SHAPE_REPRESENTATION(#12,#2262);+#14=NEXT_ASSEMBLY_USAGE_OCCURRENCE('MpinContact_Medium v6:4',+'MpinContact_Medium v6:4','MpinContact_Medium v6:4',#2266,#2267,+'MpinContact_Medium v6:4');+#15=SPHERICAL_SURFACE('',#1039,0.254999999999997);+#16=(+BOUNDED_CURVE()+B_SPLINE_CURVE(2,(#2141,#2142,#2143),.UNSPECIFIED.,.F.,.F.)+B_SPLINE_CURVE_WITH_KNOTS((3,3),(0.,0.0692820323027556),.UNSPECIFIED.)+CURVE()+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_CURVE((1.,1.20000000000001,1.))+REPRESENTATION_ITEM('')+);+#17=(+BOUNDED_CURVE()+B_SPLINE_CURVE(2,(#2152,#2153,#2154),.UNSPECIFIED.,.F.,.F.)+B_SPLINE_CURVE_WITH_KNOTS((3,3),(0.,0.0692820323027557),.UNSPECIFIED.)+CURVE()+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_CURVE((1.,1.2,1.))+REPRESENTATION_ITEM('')+);+#18=(+BOUNDED_CURVE()+B_SPLINE_CURVE(2,(#2163,#2164,#2165),.UNSPECIFIED.,.F.,.F.)+B_SPLINE_CURVE_WITH_KNOTS((3,3),(0.,0.0692820323027557),.UNSPECIFIED.)+CURVE()+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_CURVE((1.,1.2,1.))+REPRESENTATION_ITEM('')+);+#19=(+BOUNDED_CURVE()+B_SPLINE_CURVE(2,(#2174,#2175,#2176),.UNSPECIFIED.,.F.,.F.)+B_SPLINE_CURVE_WITH_KNOTS((3,3),(0.,0.0346410161513776),.UNSPECIFIED.)+CURVE()+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_CURVE((1.,1.1,1.1))+REPRESENTATION_ITEM('')+);+#20=(+BOUNDED_CURVE()+B_SPLINE_CURVE(2,(#2183,#2184,#2185),.UNSPECIFIED.,.F.,.F.)+B_SPLINE_CURVE_WITH_KNOTS((3,3),(0.0346410161513776,0.0692820323027552),+ .UNSPECIFIED.)+CURVE()+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_CURVE((1.1,1.1,1.))+REPRESENTATION_ITEM('')+);+#21=SHAPE_REPRESENTATION_RELATIONSHIP('SRR','None',#2264,#23);+#22=SHAPE_REPRESENTATION_RELATIONSHIP('SRR','None',#2263,#24);+#23=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#27),#2252);+#24=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#28),#2251);+#25=STYLED_ITEM('',(#2278),#27);+#26=STYLED_ITEM('',(#2277),#28);+#27=MANIFOLD_SOLID_BREP('Body1',#926);+#28=MANIFOLD_SOLID_BREP('Body1',#927);+#29=ELLIPSE('',#956,0.123303040813508,0.117733653429061);+#30=ELLIPSE('',#960,0.123303040810181,0.117733653427394);+#31=ELLIPSE('',#964,0.123300822678697,0.117732417193227);+#32=ELLIPSE('',#967,0.12330082267097,0.117732417188975);+#33=ELLIPSE('',#969,0.0946949526236447,0.0904878540173368);+#34=ELLIPSE('',#970,0.0946949527732227,0.0904878541006801);+#35=ELLIPSE('',#983,0.0947014027658022,0.0904914268414502);+#36=ELLIPSE('',#984,0.0947013568795386,0.0904914015032144);+#37=ELLIPSE('',#991,0.0947014129570492,0.0904914324665984);+#38=ELLIPSE('',#994,0.0946959223869005,0.0904883921275694);+#39=ELLIPSE('',#997,0.0947012752311686,0.09049135641962);+#40=ELLIPSE('',#998,0.0946959221538525,0.0904883919975931);+#41=CONICAL_SURFACE('',#954,0.3500599520011,0.140686945962564);+#42=CONICAL_SURFACE('',#957,0.3500599520011,0.140686945962564);+#43=CONICAL_SURFACE('',#961,0.3500599520011,0.140686945962564);+#44=CONICAL_SURFACE('',#965,0.3500599520011,0.140686945962564);+#45=CONICAL_SURFACE('',#968,0.0854901781157201,0.25326156346064);+#46=CONICAL_SURFACE('',#982,0.107259882566625,0.253261563450098);+#47=CONICAL_SURFACE('',#987,0.2500550224821,0.140686945962564);+#48=CONICAL_SURFACE('',#989,0.2500550224821,0.140686945962564);+#49=CONICAL_SURFACE('',#992,0.2500550224821,0.140686945962564);+#50=CONICAL_SURFACE('',#995,0.2500550224821,0.140686945962564);+#51=CONICAL_SURFACE('',#1002,0.107321319983375,0.253261563452846);+#52=CONICAL_SURFACE('',#1006,0.107259881520342,0.253261563456436);+#53=CONICAL_SURFACE('',#1024,0.517499999999998,0.785398163397449);+#54=CONICAL_SURFACE('',#1031,0.2175,1.02974425867665);+#55=CONICAL_SURFACE('',#1033,0.5175,0.785398163397448);+#56=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1373,#1374,#1375,#1376,#1377,#1378,+#1379,#1380,#1381,#1382,#1383,#1384,#1385,#1386,#1387,#1388,#1389,#1390,+#1391,#1392,#1393,#1394,#1395,#1396,#1397,#1398,#1399,#1400,#1401,#1402,+#1403,#1404,#1405,#1406,#1407,#1408,#1409,#1410,#1411,#1412,#1413,#1414,+#1415,#1416,#1417,#1418,#1419,#1420,#1421,#1422,#1423,#1424,#1425,#1426,+#1427,#1428,#1429,#1430,#1431,#1432,#1433,#1434,#1435,#1436,#1437,#1438,+#1439,#1440,#1441,#1442,#1443,#1444,#1445),.UNSPECIFIED.,.F.,.F.,(4,3,3,+3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4),(0.,0.0458743804394104,0.06666666666667,+0.111053365481494,0.1333333333333,0.187608648622586,0.2,0.2666666666667,+0.315138133753424,0.3333333333333,0.4,0.4666666666667,0.500062603112874,+0.5333333333333,0.6,0.6666666666667,0.71505202633289,0.7333333333333,0.8,+0.852985530890195,0.8666666666667,0.909980948728713,0.9333333333333,0.978752310399226,+1.),.UNSPECIFIED.);+#57=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1451,#1452,#1453,#1454,#1455,#1456,+#1457,#1458,#1459,#1460,#1461,#1462,#1463,#1464,#1465,#1466,#1467,#1468,+#1469,#1470,#1471,#1472,#1473,#1474,#1475,#1476,#1477,#1478,#1479,#1480,+#1481,#1482,#1483,#1484),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,3,4),+(0.,0.110670393651878,0.1428571428571,0.266027911824754,0.2857142857143,+0.4285714285714,0.555914630849129,0.5714285714286,0.7142857142857,0.8571428571429,+0.972337169516299,1.),.UNSPECIFIED.);+#58=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1488,#1489,#1490,#1491,#1492,#1493,+#1494,#1495,#1496,#1497,#1498,#1499,#1500,#1501,#1502,#1503,#1504,#1505,+#1506,#1507,#1508,#1509,#1510,#1511,#1512,#1513,#1514,#1515,#1516,#1517,+#1518,#1519,#1520,#1521),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,3,4),+(0.,0.115194312373676,0.1428571428571,0.2857142857143,0.4285714285714,0.555914630850625,+0.5714285714286,0.7142857142857,0.837456483254619,0.8571428571429,0.967813250795107,+1.),.UNSPECIFIED.);+#59=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1563,#1564,#1565,#1566,#1567,#1568,+#1569,#1570,#1571,#1572,#1573,#1574,#1575,#1576,#1577,#1578,#1579,#1580,+#1581,#1582,#1583,#1584,#1585,#1586,#1587,#1588,#1589,#1590,#1591,#1592,+#1593,#1594,#1595,#1596),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,3,4),+(0.,0.115194312373344,0.1428571428571,0.2857142857143,0.4285714285714,0.55591463084967,+0.5714285714286,0.7142857142857,0.837456483253965,0.8571428571429,0.967813250794925,+1.),.UNSPECIFIED.);+#60=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1602,#1603,#1604,#1605,#1606,#1607,+#1608,#1609,#1610,#1611,#1612,#1613,#1614,#1615,#1616,#1617,#1618,#1619,+#1620,#1621,#1622,#1623,#1624,#1625,#1626,#1627,#1628,#1629,#1630,#1631,+#1632),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,4),(0.,0.11760285124474,+0.1428571428571,0.2857142857143,0.4285714285714,0.5714285714286,0.7142857142857,+0.837110761937757,0.8571428571429,0.968740340808658,1.),.UNSPECIFIED.);+#61=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1634,#1635,#1636,#1637,#1638,#1639,+#1640,#1641,#1642,#1643,#1644,#1645,#1646,#1647,#1648,#1649,#1650,#1651,+#1652,#1653,#1654,#1655,#1656,#1657,#1658,#1659,#1660,#1661,#1662,#1663,+#1664),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,4),(0.,0.111620281731672,+0.1428571428571,0.265837725142949,0.2857142857143,0.4285714285714,0.5714285714286,+0.7142857142857,0.8571428571429,0.974754997803736,1.),.UNSPECIFIED.);+#62=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1705,#1706,#1707,#1708,#1709,#1710,+#1711,#1712,#1713,#1714,#1715,#1716,#1717,#1718,#1719,#1720,#1721,#1722,+#1723,#1724,#1725,#1726,#1727,#1728,#1729,#1730,#1731,#1732,#1733,#1734,+#1735,#1736,#1737,#1738,#1739,#1740,#1741,#1742,#1743,#1744,#1745,#1746,+#1747,#1748,#1749,#1750,#1751,#1752,#1753,#1754,#1755,#1756,#1757,#1758,+#1759,#1760,#1761,#1762,#1763,#1764,#1765,#1766,#1767,#1768,#1769,#1770,+#1771,#1772,#1773,#1774,#1775,#1776,#1777,#1778,#1779,#1780,#1781,#1782,+#1783,#1784,#1785,#1786,#1787,#1788,#1789),.UNSPECIFIED.,.F.,.F.,(4,3,3,+3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4),(0.,0.0502534832476538,+0.06666666666667,0.120253593186299,0.1333333333333,0.2,0.255749513845402,+0.2666666666667,0.329367686983086,0.3333333333333,0.395836562108083,0.4,+0.436524911771876,0.452451414206895,0.4666666666667,0.515622988915972,0.5333333333333,+0.592226745628581,0.6,0.6666666666667,0.7333333333333,0.780353991035205,+0.8,0.852291238583784,0.8666666666667,0.911018075852901,0.9333333333333,+0.986479892247513,1.),.UNSPECIFIED.);+#63=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1791,#1792,#1793,#1794,#1795,#1796,+#1797,#1798,#1799,#1800,#1801,#1802,#1803,#1804,#1805,#1806,#1807,#1808,+#1809,#1810,#1811,#1812,#1813,#1814,#1815,#1816,#1817,#1818,#1819,#1820,+#1821,#1822,#1823,#1824,#1825,#1826,#1827,#1828,#1829,#1830,#1831,#1832,+#1833,#1834,#1835,#1836,#1837,#1838,#1839,#1840,#1841,#1842,#1843,#1844,+#1845,#1846,#1847,#1848,#1849,#1850,#1851,#1852,#1853,#1854,#1855,#1856,+#1857,#1858,#1859,#1860,#1861,#1862,#1863),.UNSPECIFIED.,.F.,.F.,(4,3,3,+3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4),(0.,0.04588956397002,0.06666666666667,+0.111096932061863,0.1333333333333,0.187622258209603,0.2,0.2666666666667,+0.315146604896394,0.3333333333333,0.4,0.4666666666667,0.500057216242827,+0.5333333333333,0.6,0.6666666666667,0.715062394803326,0.7333333333333,0.8,+0.853040552252666,0.8666666666667,0.910029166730225,0.9333333333333,0.97877240463691,+1.),.UNSPECIFIED.);+#64=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1913,#1914,#1915,#1916,#1917,#1918,+#1919,#1920,#1921,#1922,#1923,#1924,#1925,#1926,#1927,#1928,#1929,#1930,+#1931,#1932,#1933,#1934,#1935,#1936,#1937,#1938,#1939,#1940,#1941,#1942,+#1943),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,4),(0.,0.117602851245036,+0.1428571428571,0.2857142857143,0.4285714285714,0.5714285714286,0.7142857142857,+0.837110761938758,0.8571428571429,0.968740340808622,1.),.UNSPECIFIED.);+#65=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1946,#1947,#1948,#1949,#1950,#1951,+#1952,#1953,#1954,#1955,#1956,#1957,#1958,#1959,#1960,#1961,#1962,#1963,+#1964,#1965,#1966,#1967,#1968,#1969,#1970,#1971,#1972,#1973,#1974,#1975,+#1976),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,4),(0.,0.111620281731597,+0.1428571428571,0.265837725143241,0.2857142857143,0.4285714285714,0.5714285714286,+0.7142857142857,0.8571428571429,0.974754997804418,1.),.UNSPECIFIED.);+#66=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1978,#1979,#1980,#1981,#1982,#1983,+#1984,#1985,#1986,#1987,#1988,#1989,#1990,#1991,#1992,#1993,#1994,#1995,+#1996,#1997,#1998,#1999,#2000,#2001,#2002,#2003,#2004,#2005,#2006,#2007,+#2008,#2009,#2010,#2011),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,3,4),+(0.,0.110670393652087,0.1428571428571,0.266027911825212,0.2857142857143,+0.4285714285714,0.555914630849822,0.5714285714286,0.7142857142857,0.8571428571429,+0.972337169518356,1.),.UNSPECIFIED.);+#67=B_SPLINE_CURVE_WITH_KNOTS('',3,(#2045,#2046,#2047,#2048,#2049,#2050,+#2051,#2052,#2053,#2054,#2055,#2056,#2057,#2058,#2059,#2060,#2061,#2062,+#2063,#2064,#2065,#2066,#2067,#2068,#2069,#2070,#2071,#2072,#2073,#2074,+#2075,#2076,#2077,#2078,#2079,#2080,#2081,#2082,#2083,#2084,#2085,#2086,+#2087,#2088,#2089,#2090,#2091,#2092,#2093,#2094,#2095,#2096,#2097,#2098,+#2099,#2100,#2101,#2102,#2103,#2104,#2105,#2106,#2107,#2108,#2109,#2110,+#2111,#2112,#2113,#2114,#2115,#2116,#2117,#2118,#2119,#2120,#2121,#2122,+#2123,#2124,#2125,#2126,#2127,#2128,#2129),.UNSPECIFIED.,.F.,.F.,(4,3,3,+3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4),(0.,0.0502991796550745,+0.06666666666667,0.120366811112814,0.1333333333333,0.2,0.255766519209152,+0.2666666666667,0.32947887253912,0.3333333333333,0.396068147196848,0.4,+0.436656907057471,0.452683676537785,0.4666666666667,0.515757169029327,0.5333333333333,+0.592124003931906,0.6,0.6666666666667,0.7333333333333,0.780356675190033,+0.8,0.852111141982603,0.8666666666667,0.911050384152092,0.9333333333333,+0.986496853314226,1.),.UNSPECIFIED.);+#68=(+BOUNDED_SURFACE()+B_SPLINE_SURFACE(2,2,((#1352,#1353,#1354,#1355,#1356),(#1357,#1358,#1359,+#1360,#1361),(#1362,#1363,#1364,#1365,#1366)),.UNSPECIFIED.,.F.,.F.,.F.)+B_SPLINE_SURFACE_WITH_KNOTS((3,3),(3,2,3),(-0.163922449712767,-4.36520356989735E-9),+(-3.14159265417117,-1.5707963267949,5.81380277253629E-10),.UNSPECIFIED.)+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_SURFACE(((1.,0.707106780980999,1.,0.707106780980999,1.),+(0.996643058847661,0.704733065128826,0.996643058847661,0.704733065128826,+0.996643058847661),(1.,0.707106780980999,1.,0.707106780980999,1.)))+REPRESENTATION_ITEM('')+SURFACE()+);+#69=(+BOUNDED_SURFACE()+B_SPLINE_SURFACE(2,2,((#1892,#1893,#1894,#1895,#1896),(#1897,#1898,#1899,+#1900,#1901),(#1902,#1903,#1904,#1905,#1906)),.UNSPECIFIED.,.F.,.F.,.F.)+B_SPLINE_SURFACE_WITH_KNOTS((3,3),(3,2,3),(-0.140686961663467,-2.71515528509551E-9),+(-6.28318530778024,-4.71238898038469,-3.14159265298914),.UNSPECIFIED.)+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_SURFACE(((1.,0.707106780974184,1.,0.707106780974184,1.),+(0.997526917476737,0.705358047552076,0.997526917476737,0.705358047552076,+0.997526917476737),(1.,0.707106780974184,1.,0.707106780974184,1.)))+REPRESENTATION_ITEM('')+SURFACE()+);+#70=(+BOUNDED_SURFACE()+B_SPLINE_SURFACE(2,2,((#2013,#2014,#2015,#2016,#2017),(#2018,#2019,#2020,+#2021,#2022),(#2023,#2024,#2025,#2026,#2027)),.UNSPECIFIED.,.F.,.F.,.F.)+B_SPLINE_SURFACE_WITH_KNOTS((3,3),(3,2,3),(-0.163922449712767,-4.36520356989735E-9),+(-6.28318530776097,-4.71238898038469,-3.14159265300841),.UNSPECIFIED.)+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_SURFACE(((1.,0.707106780980999,1.,0.707106780980999,1.),+(0.99664305884761,0.70473306512879,0.99664305884761,0.70473306512879,0.99664305884761),+(1.,0.707106780980999,1.,0.707106780980999,1.)))+REPRESENTATION_ITEM('')+SURFACE()+);+#71=(+BOUNDED_SURFACE()+B_SPLINE_SURFACE(2,2,((#2029,#2030,#2031,#2032,#2033),(#2034,#2035,#2036,+#2037,#2038),(#2039,#2040,#2041,#2042,#2043)),.UNSPECIFIED.,.F.,.F.,.F.)+B_SPLINE_SURFACE_WITH_KNOTS((3,3),(3,2,3),(-0.140686961663467,-2.71515528509551E-9),+(-3.14159265419045,-1.5707963267949,6.00655525317961E-10),.UNSPECIFIED.)+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_SURFACE(((1.,0.707106780974184,1.,0.707106780974184,1.),+(0.997526917476699,0.705358047552049,0.997526917476699,0.705358047552049,+0.997526917476699),(1.,0.707106780974184,1.,0.707106780974184,1.)))+REPRESENTATION_ITEM('')+SURFACE()+);+#72=LINE('',#1351,#126);+#73=LINE('',#1524,#127);+#74=LINE('',#1528,#128);+#75=LINE('',#1533,#129);+#76=LINE('',#1537,#130);+#77=LINE('',#1546,#131);+#78=LINE('',#1550,#132);+#79=LINE('',#1555,#133);+#80=LINE('',#1558,#134);+#81=LINE('',#1562,#135);+#82=LINE('',#1598,#136);+#83=LINE('',#1668,#137);+#84=LINE('',#1669,#138);+#85=LINE('',#1672,#139);+#86=LINE('',#1675,#140);+#87=LINE('',#1679,#141);+#88=LINE('',#1682,#142);+#89=LINE('',#1684,#143);+#90=LINE('',#1688,#144);+#91=LINE('',#1691,#145);+#92=LINE('',#1695,#146);+#93=LINE('',#1696,#147);+#94=LINE('',#1701,#148);+#95=LINE('',#1866,#149);+#96=LINE('',#1872,#150);+#97=LINE('',#1874,#151);+#98=LINE('',#1881,#152);+#99=LINE('',#1888,#153);+#100=LINE('',#1890,#154);+#101=LINE('',#1945,#155);+#102=LINE('',#1977,#156);+#103=LINE('',#2131,#157);+#104=LINE('',#2132,#158);+#105=LINE('',#2136,#159);+#106=LINE('',#2145,#160);+#107=LINE('',#2147,#161);+#108=LINE('',#2148,#162);+#109=LINE('',#2156,#163);+#110=LINE('',#2158,#164);+#111=LINE('',#2159,#165);+#112=LINE('',#2167,#166);+#113=LINE('',#2169,#167);+#114=LINE('',#2170,#168);+#115=LINE('',#2178,#169);+#116=LINE('',#2180,#170);+#117=LINE('',#2182,#171);+#118=LINE('',#2198,#172);+#119=LINE('',#2209,#173);+#120=LINE('',#2214,#174);+#121=LINE('',#2217,#175);+#122=LINE('',#2227,#176);+#123=LINE('',#2234,#177);+#124=LINE('',#2241,#178);+#125=LINE('',#2246,#179);+#126=VECTOR('',#1071,0.435);+#127=VECTOR('',#1086,1.07057740755999);+#128=VECTOR('',#1089,1.04731117564899);+#129=VECTOR('',#1094,1.04731117564899);+#130=VECTOR('',#1097,1.07057740755999);+#131=VECTOR('',#1106,1.07057740755999);+#132=VECTOR('',#1109,1.04731117564899);+#133=VECTOR('',#1114,1.04731117564899);+#134=VECTOR('',#1117,1.07057740755999);+#135=VECTOR('',#1122,10.);+#136=VECTOR('',#1123,10.);+#137=VECTOR('',#1130,1.07364424125699);+#138=VECTOR('',#1131,0.100997868637703);+#139=VECTOR('',#1134,0.100997868637703);+#140=VECTOR('',#1137,0.100997868637703);+#141=VECTOR('',#1142,0.100997868637703);+#142=VECTOR('',#1145,0.100997868637703);+#143=VECTOR('',#1146,0.100997868637703);+#144=VECTOR('',#1151,0.100997868637703);+#145=VECTOR('',#1154,0.100997868637703);+#146=VECTOR('',#1157,10.);+#147=VECTOR('',#1158,1.07364424125699);+#148=VECTOR('',#1163,10.);+#149=VECTOR('',#1172,1.07364424125699);+#150=VECTOR('',#1181,1.07364424125699);+#151=VECTOR('',#1182,1.07364424125699);+#152=VECTOR('',#1191,1.07364424125699);+#153=VECTOR('',#1198,1.07364424125699);+#154=VECTOR('',#1199,1.07364424125699);+#155=VECTOR('',#1210,10.);+#156=VECTOR('',#1211,10.);+#157=VECTOR('',#1220,10.);+#158=VECTOR('',#1221,10.);+#159=VECTOR('',#1228,0.335);+#160=VECTOR('',#1233,10.);+#161=VECTOR('',#1234,10.);+#162=VECTOR('',#1235,10.);+#163=VECTOR('',#1238,10.);+#164=VECTOR('',#1239,10.);+#165=VECTOR('',#1240,10.);+#166=VECTOR('',#1243,10.);+#167=VECTOR('',#1244,10.);+#168=VECTOR('',#1245,10.);+#169=VECTOR('',#1248,10.);+#170=VECTOR('',#1249,10.);+#171=VECTOR('',#1250,10.);+#172=VECTOR('',#1273,0.517499999999997);+#173=VECTOR('',#1288,0.2175);+#174=VECTOR('',#1293,0.5175);+#175=VECTOR('',#1298,0.435);+#176=VECTOR('',#1311,0.254999999999997);+#177=VECTOR('',#1320,0.799999999999999);+#178=VECTOR('',#1329,0.599999999999999);+#179=VECTOR('',#1336,0.800000000000001);+#180=CYLINDRICAL_SURFACE('',#945,0.435);+#181=CYLINDRICAL_SURFACE('',#1009,0.335);+#182=CYLINDRICAL_SURFACE('',#1015,0.599999999999998);+#183=CYLINDRICAL_SURFACE('',#1018,0.599999999999998);+#184=CYLINDRICAL_SURFACE('',#1021,0.599999999999998);+#185=CYLINDRICAL_SURFACE('',#1029,0.599999999999998);+#186=CYLINDRICAL_SURFACE('',#1036,0.435);+#187=CYLINDRICAL_SURFACE('',#1042,0.254999999999997);+#188=CYLINDRICAL_SURFACE('',#1045,0.799999999999999);+#189=CYLINDRICAL_SURFACE('',#1049,0.599999999999999);+#190=CYLINDRICAL_SURFACE('',#1053,0.800000000000001);+#191=FACE_BOUND('',#251,.T.);+#192=FACE_BOUND('',#291,.T.);+#193=FACE_BOUND('',#297,.T.);+#194=FACE_BOUND('',#301,.T.);+#195=FACE_BOUND('',#304,.T.);+#196=FACE_BOUND('',#307,.T.);+#197=FACE_OUTER_BOUND('',#250,.T.);+#198=FACE_OUTER_BOUND('',#252,.T.);+#199=FACE_OUTER_BOUND('',#253,.T.);+#200=FACE_OUTER_BOUND('',#254,.T.);+#201=FACE_OUTER_BOUND('',#255,.T.);+#202=FACE_OUTER_BOUND('',#256,.T.);+#203=FACE_OUTER_BOUND('',#257,.T.);+#204=FACE_OUTER_BOUND('',#258,.T.);+#205=FACE_OUTER_BOUND('',#259,.T.);+#206=FACE_OUTER_BOUND('',#260,.T.);+#207=FACE_OUTER_BOUND('',#261,.T.);+#208=FACE_OUTER_BOUND('',#262,.T.);+#209=FACE_OUTER_BOUND('',#263,.T.);+#210=FACE_OUTER_BOUND('',#264,.T.);+#211=FACE_OUTER_BOUND('',#265,.T.);+#212=FACE_OUTER_BOUND('',#266,.T.);+#213=FACE_OUTER_BOUND('',#267,.T.);+#214=FACE_OUTER_BOUND('',#268,.T.);+#215=FACE_OUTER_BOUND('',#269,.T.);+#216=FACE_OUTER_BOUND('',#270,.T.);+#217=FACE_OUTER_BOUND('',#271,.T.);+#218=FACE_OUTER_BOUND('',#272,.T.);+#219=FACE_OUTER_BOUND('',#273,.T.);+#220=FACE_OUTER_BOUND('',#274,.T.);+#221=FACE_OUTER_BOUND('',#275,.T.);+#222=FACE_OUTER_BOUND('',#276,.T.);+#223=FACE_OUTER_BOUND('',#277,.T.);+#224=FACE_OUTER_BOUND('',#278,.T.);+#225=FACE_OUTER_BOUND('',#279,.T.);+#226=FACE_OUTER_BOUND('',#280,.T.);+#227=FACE_OUTER_BOUND('',#281,.T.);+#228=FACE_OUTER_BOUND('',#282,.T.);+#229=FACE_OUTER_BOUND('',#283,.T.);+#230=FACE_OUTER_BOUND('',#284,.T.);+#231=FACE_OUTER_BOUND('',#285,.T.);+#232=FACE_OUTER_BOUND('',#286,.T.);+#233=FACE_OUTER_BOUND('',#287,.T.);+#234=FACE_OUTER_BOUND('',#288,.T.);+#235=FACE_OUTER_BOUND('',#289,.T.);+#236=FACE_OUTER_BOUND('',#290,.T.);+#237=FACE_OUTER_BOUND('',#292,.T.);+#238=FACE_OUTER_BOUND('',#293,.T.);+#239=FACE_OUTER_BOUND('',#294,.T.);+#240=FACE_OUTER_BOUND('',#295,.T.);+#241=FACE_OUTER_BOUND('',#296,.T.);+#242=FACE_OUTER_BOUND('',#298,.T.);+#243=FACE_OUTER_BOUND('',#299,.T.);+#244=FACE_OUTER_BOUND('',#300,.T.);+#245=FACE_OUTER_BOUND('',#302,.T.);+#246=FACE_OUTER_BOUND('',#303,.T.);+#247=FACE_OUTER_BOUND('',#305,.T.);+#248=FACE_OUTER_BOUND('',#306,.T.);+#249=FACE_OUTER_BOUND('',#308,.T.);+#250=EDGE_LOOP('',(#583));+#251=EDGE_LOOP('',(#584));+#252=EDGE_LOOP('',(#585,#586,#587,#588,#589));+#253=EDGE_LOOP('',(#590,#591,#592,#593,#594,#595,#596,#597,#598,#599));+#254=EDGE_LOOP('',(#600,#601,#602,#603,#604));+#255=EDGE_LOOP('',(#605,#606,#607,#608,#609));+#256=EDGE_LOOP('',(#610,#611,#612,#613,#614));+#257=EDGE_LOOP('',(#615,#616,#617,#618,#619));+#258=EDGE_LOOP('',(#620,#621,#622,#623,#624,#625,#626,#627,#628,#629));+#259=EDGE_LOOP('',(#630,#631,#632,#633));+#260=EDGE_LOOP('',(#634,#635,#636,#637));+#261=EDGE_LOOP('',(#638,#639,#640,#641));+#262=EDGE_LOOP('',(#642,#643,#644,#645));+#263=EDGE_LOOP('',(#646,#647,#648,#649));+#264=EDGE_LOOP('',(#650,#651,#652,#653,#654));+#265=EDGE_LOOP('',(#655,#656,#657,#658,#659,#660));+#266=EDGE_LOOP('',(#661,#662,#663,#664,#665));+#267=EDGE_LOOP('',(#666,#667,#668,#669,#670,#671));+#268=EDGE_LOOP('',(#672,#673,#674,#675,#676,#677));+#269=EDGE_LOOP('',(#678,#679,#680,#681,#682,#683));+#270=EDGE_LOOP('',(#684,#685,#686,#687,#688,#689));+#271=EDGE_LOOP('',(#690,#691,#692,#693,#694,#695,#696,#697));+#272=EDGE_LOOP('',(#698,#699,#700,#701,#702,#703,#704,#705,#706,#707));+#273=EDGE_LOOP('',(#708,#709,#710,#711));+#274=EDGE_LOOP('',(#712,#713,#714,#715,#716,#717,#718,#719,#720,#721));+#275=EDGE_LOOP('',(#722,#723,#724,#725));+#276=EDGE_LOOP('',(#726,#727,#728,#729,#730,#731,#732,#733));+#277=EDGE_LOOP('',(#734,#735,#736,#737,#738,#739));+#278=EDGE_LOOP('',(#740,#741,#742,#743,#744));+#279=EDGE_LOOP('',(#745,#746,#747,#748,#749));+#280=EDGE_LOOP('',(#750,#751,#752,#753,#754));+#281=EDGE_LOOP('',(#755,#756,#757,#758));+#282=EDGE_LOOP('',(#759,#760,#761,#762));+#283=EDGE_LOOP('',(#763,#764,#765,#766));+#284=EDGE_LOOP('',(#767,#768,#769,#770));+#285=EDGE_LOOP('',(#771,#772,#773,#774,#775));+#286=EDGE_LOOP('',(#776,#777,#778,#779));+#287=EDGE_LOOP('',(#780,#781,#782,#783));+#288=EDGE_LOOP('',(#784,#785,#786,#787));+#289=EDGE_LOOP('',(#788,#789,#790,#791,#792,#793,#794,#795,#796,#797,#798,+#799));+#290=EDGE_LOOP('',(#800));+#291=EDGE_LOOP('',(#801));+#292=EDGE_LOOP('',(#802,#803,#804,#805));+#293=EDGE_LOOP('',(#806,#807,#808));+#294=EDGE_LOOP('',(#809,#810,#811,#812));+#295=EDGE_LOOP('',(#813,#814,#815,#816));+#296=EDGE_LOOP('',(#817));+#297=EDGE_LOOP('',(#818));+#298=EDGE_LOOP('',(#819,#820,#821));+#299=EDGE_LOOP('',(#822,#823,#824,#825));+#300=EDGE_LOOP('',(#826));+#301=EDGE_LOOP('',(#827,#828,#829,#830,#831,#832,#833,#834));+#302=EDGE_LOOP('',(#835,#836,#837,#838));+#303=EDGE_LOOP('',(#839));+#304=EDGE_LOOP('',(#840));+#305=EDGE_LOOP('',(#841,#842,#843,#844));+#306=EDGE_LOOP('',(#845));+#307=EDGE_LOOP('',(#846));+#308=EDGE_LOOP('',(#847,#848,#849,#850));+#309=CIRCLE('',#943,0.435);+#310=CIRCLE('',#944,0.335);+#311=CIRCLE('',#946,0.435);+#312=CIRCLE('',#947,0.435);+#313=CIRCLE('',#948,0.4251199040021);+#314=CIRCLE('',#949,1.);+#315=CIRCLE('',#950,1.);+#316=CIRCLE('',#951,0.4251199040021);+#317=CIRCLE('',#952,1.);+#318=CIRCLE('',#953,1.);+#319=CIRCLE('',#955,0.275);+#320=CIRCLE('',#958,0.275);+#321=CIRCLE('',#959,0.4251199040021);+#322=CIRCLE('',#962,0.4251199040021);+#323=CIRCLE('',#963,0.275);+#324=CIRCLE('',#966,0.275);+#325=CIRCLE('',#973,0.1740021313623);+#326=CIRCLE('',#975,0.1740021313623);+#327=CIRCLE('',#977,0.1740021313623);+#328=CIRCLE('',#979,0.1740021313623);+#329=CIRCLE('',#981,1.);+#330=CIRCLE('',#986,1.);+#331=CIRCLE('',#988,0.3261079136019);+#332=CIRCLE('',#990,0.3261079136019);+#333=CIRCLE('',#993,0.3261079136019);+#334=CIRCLE('',#996,0.3261079136019);+#335=CIRCLE('',#999,0.9);+#336=CIRCLE('',#1000,0.335);+#337=CIRCLE('',#1001,0.9);+#338=CIRCLE('',#1005,0.335);+#339=CIRCLE('',#1016,0.599999999999998);+#340=CIRCLE('',#1017,0.599999999999999);+#341=CIRCLE('',#1019,0.599999999999998);+#342=CIRCLE('',#1020,0.599999999999999);+#343=CIRCLE('',#1022,0.599999999999999);+#344=CIRCLE('',#1023,0.599999999999998);+#345=CIRCLE('',#1025,0.599999999999998);+#346=CIRCLE('',#1026,0.434999999999997);+#347=CIRCLE('',#1028,0.254999999999998);+#348=CIRCLE('',#1030,0.599999999999999);+#349=CIRCLE('',#1032,0.435);+#350=CIRCLE('',#1034,0.6);+#351=CIRCLE('',#1035,0.435);+#352=CIRCLE('',#1038,0.800000000000002);+#353=CIRCLE('',#1040,0.254999999999997);+#354=CIRCLE('',#1041,0.254999999999997);+#355=CIRCLE('',#1044,0.799999999999999);+#356=CIRCLE('',#1046,0.799999999999999);+#357=CIRCLE('',#1048,0.599999999999998);+#358=CIRCLE('',#1050,0.599999999999999);+#359=CIRCLE('',#1052,0.8);+#360=VERTEX_POINT('',#1342);+#361=VERTEX_POINT('',#1344);+#362=VERTEX_POINT('',#1347);+#363=VERTEX_POINT('',#1348);+#364=VERTEX_POINT('',#1367);+#365=VERTEX_POINT('',#1368);+#366=VERTEX_POINT('',#1370);+#367=VERTEX_POINT('',#1372);+#368=VERTEX_POINT('',#1446);+#369=VERTEX_POINT('',#1448);+#370=VERTEX_POINT('',#1450);+#371=VERTEX_POINT('',#1486);+#372=VERTEX_POINT('',#1523);+#373=VERTEX_POINT('',#1525);+#374=VERTEX_POINT('',#1527);+#375=VERTEX_POINT('',#1531);+#376=VERTEX_POINT('',#1532);+#377=VERTEX_POINT('',#1534);+#378=VERTEX_POINT('',#1536);+#379=VERTEX_POINT('',#1538);+#380=VERTEX_POINT('',#1542);+#381=VERTEX_POINT('',#1543);+#382=VERTEX_POINT('',#1545);+#383=VERTEX_POINT('',#1547);+#384=VERTEX_POINT('',#1549);+#385=VERTEX_POINT('',#1553);+#386=VERTEX_POINT('',#1554);+#387=VERTEX_POINT('',#1556);+#388=VERTEX_POINT('',#1561);+#389=VERTEX_POINT('',#1597);+#390=VERTEX_POINT('',#1599);+#391=VERTEX_POINT('',#1601);+#392=VERTEX_POINT('',#1633);+#393=VERTEX_POINT('',#1667);+#394=VERTEX_POINT('',#1671);+#395=VERTEX_POINT('',#1673);+#396=VERTEX_POINT('',#1677);+#397=VERTEX_POINT('',#1681);+#398=VERTEX_POINT('',#1683);+#399=VERTEX_POINT('',#1687);+#400=VERTEX_POINT('',#1689);+#401=VERTEX_POINT('',#1693);+#402=VERTEX_POINT('',#1694);+#403=VERTEX_POINT('',#1699);+#404=VERTEX_POINT('',#1700);+#405=VERTEX_POINT('',#1702);+#406=VERTEX_POINT('',#1704);+#407=VERTEX_POINT('',#1870);+#408=VERTEX_POINT('',#1873);+#409=VERTEX_POINT('',#1877);+#410=VERTEX_POINT('',#1879);+#411=VERTEX_POINT('',#1883);+#412=VERTEX_POINT('',#1884);+#413=VERTEX_POINT('',#1886);+#414=VERTEX_POINT('',#1889);+#415=VERTEX_POINT('',#1907);+#416=VERTEX_POINT('',#1909);+#417=VERTEX_POINT('',#1911);+#418=VERTEX_POINT('',#2139);+#419=VERTEX_POINT('',#2140);+#420=VERTEX_POINT('',#2144);+#421=VERTEX_POINT('',#2146);+#422=VERTEX_POINT('',#2150);+#423=VERTEX_POINT('',#2151);+#424=VERTEX_POINT('',#2155);+#425=VERTEX_POINT('',#2157);+#426=VERTEX_POINT('',#2161);+#427=VERTEX_POINT('',#2162);+#428=VERTEX_POINT('',#2166);+#429=VERTEX_POINT('',#2168);+#430=VERTEX_POINT('',#2172);+#431=VERTEX_POINT('',#2173);+#432=VERTEX_POINT('',#2177);+#433=VERTEX_POINT('',#2179);+#434=VERTEX_POINT('',#2181);+#435=VERTEX_POINT('',#2197);+#436=VERTEX_POINT('',#2201);+#437=VERTEX_POINT('',#2206);+#438=VERTEX_POINT('',#2208);+#439=VERTEX_POINT('',#2211);+#440=VERTEX_POINT('',#2213);+#441=VERTEX_POINT('',#2219);+#442=VERTEX_POINT('',#2222);+#443=VERTEX_POINT('',#2224);+#444=VERTEX_POINT('',#2229);+#445=VERTEX_POINT('',#2232);+#446=VERTEX_POINT('',#2236);+#447=VERTEX_POINT('',#2239);+#448=VERTEX_POINT('',#2243);+#449=EDGE_CURVE('',#360,#360,#309,.T.);+#450=EDGE_CURVE('',#361,#361,#310,.T.);+#451=EDGE_CURVE('',#362,#363,#311,.T.);+#452=EDGE_CURVE('',#362,#363,#312,.T.);+#453=EDGE_CURVE('',#363,#360,#72,.T.);+#454=EDGE_CURVE('',#364,#365,#313,.T.);+#455=EDGE_CURVE('',#366,#364,#314,.T.);+#456=EDGE_CURVE('',#366,#367,#56,.T.);+#457=EDGE_CURVE('',#367,#368,#315,.T.);+#458=EDGE_CURVE('',#369,#368,#316,.T.);+#459=EDGE_CURVE('',#369,#370,#57,.T.);+#460=EDGE_CURVE('',#362,#370,#317,.T.);+#461=EDGE_CURVE('',#363,#371,#318,.T.);+#462=EDGE_CURVE('',#371,#365,#58,.T.);+#463=EDGE_CURVE('',#368,#372,#73,.T.);+#464=EDGE_CURVE('',#373,#372,#319,.T.);+#465=EDGE_CURVE('',#374,#373,#74,.T.);+#466=EDGE_CURVE('',#374,#369,#29,.T.);+#467=EDGE_CURVE('',#375,#376,#75,.T.);+#468=EDGE_CURVE('',#377,#376,#320,.T.);+#469=EDGE_CURVE('',#378,#377,#76,.T.);+#470=EDGE_CURVE('',#378,#379,#321,.T.);+#471=EDGE_CURVE('',#379,#375,#30,.T.);+#472=EDGE_CURVE('',#380,#381,#322,.T.);+#473=EDGE_CURVE('',#381,#382,#77,.T.);+#474=EDGE_CURVE('',#383,#382,#323,.T.);+#475=EDGE_CURVE('',#384,#383,#78,.T.);+#476=EDGE_CURVE('',#384,#380,#31,.T.);+#477=EDGE_CURVE('',#385,#386,#79,.T.);+#478=EDGE_CURVE('',#387,#386,#324,.T.);+#479=EDGE_CURVE('',#364,#387,#80,.T.);+#480=EDGE_CURVE('',#365,#385,#32,.T.);+#481=EDGE_CURVE('',#388,#375,#81,.T.);+#482=EDGE_CURVE('',#370,#379,#59,.T.);+#483=EDGE_CURVE('',#389,#374,#82,.T.);+#484=EDGE_CURVE('',#390,#389,#33,.T.);+#485=EDGE_CURVE('',#391,#390,#60,.T.);+#486=EDGE_CURVE('',#392,#391,#61,.T.);+#487=EDGE_CURVE('',#388,#392,#34,.T.);+#488=EDGE_CURVE('',#393,#388,#83,.T.);+#489=EDGE_CURVE('',#393,#376,#84,.T.);+#490=EDGE_CURVE('',#394,#382,#85,.T.);+#491=EDGE_CURVE('',#394,#395,#325,.T.);+#492=EDGE_CURVE('',#383,#395,#86,.T.);+#493=EDGE_CURVE('',#393,#396,#326,.T.);+#494=EDGE_CURVE('',#377,#396,#87,.T.);+#495=EDGE_CURVE('',#387,#397,#88,.T.);+#496=EDGE_CURVE('',#398,#386,#89,.T.);+#497=EDGE_CURVE('',#398,#397,#327,.T.);+#498=EDGE_CURVE('',#399,#372,#90,.T.);+#499=EDGE_CURVE('',#399,#400,#328,.T.);+#500=EDGE_CURVE('',#373,#400,#91,.T.);+#501=EDGE_CURVE('',#401,#402,#92,.T.);+#502=EDGE_CURVE('',#394,#402,#93,.T.);+#503=EDGE_CURVE('',#401,#381,#329,.T.);+#504=EDGE_CURVE('',#403,#404,#94,.T.);+#505=EDGE_CURVE('',#405,#404,#35,.T.);+#506=EDGE_CURVE('',#406,#405,#62,.T.);+#507=EDGE_CURVE('',#402,#406,#36,.T.);+#508=EDGE_CURVE('',#403,#401,#63,.T.);+#509=EDGE_CURVE('',#403,#378,#330,.T.);+#510=EDGE_CURVE('',#404,#396,#95,.T.);+#511=EDGE_CURVE('',#392,#405,#331,.T.);+#512=EDGE_CURVE('',#407,#390,#332,.T.);+#513=EDGE_CURVE('',#389,#400,#96,.T.);+#514=EDGE_CURVE('',#399,#408,#97,.T.);+#515=EDGE_CURVE('',#408,#407,#37,.T.);+#516=EDGE_CURVE('',#406,#409,#333,.T.);+#517=EDGE_CURVE('',#409,#410,#38,.T.);+#518=EDGE_CURVE('',#410,#395,#98,.T.);+#519=EDGE_CURVE('',#411,#412,#334,.T.);+#520=EDGE_CURVE('',#412,#413,#39,.T.);+#521=EDGE_CURVE('',#413,#397,#99,.T.);+#522=EDGE_CURVE('',#398,#414,#100,.T.);+#523=EDGE_CURVE('',#414,#411,#40,.T.);+#524=EDGE_CURVE('',#415,#391,#335,.T.);+#525=EDGE_CURVE('',#415,#416,#336,.T.);+#526=EDGE_CURVE('',#416,#417,#337,.T.);+#527=EDGE_CURVE('',#417,#409,#64,.T.);+#528=EDGE_CURVE('',#384,#410,#101,.T.);+#529=EDGE_CURVE('',#411,#417,#65,.T.);+#530=EDGE_CURVE('',#385,#414,#102,.T.);+#531=EDGE_CURVE('',#380,#371,#66,.T.);+#532=EDGE_CURVE('',#415,#416,#338,.T.);+#533=EDGE_CURVE('',#407,#412,#67,.T.);+#534=EDGE_CURVE('',#366,#413,#103,.T.);+#535=EDGE_CURVE('',#367,#408,#104,.T.);+#536=EDGE_CURVE('',#361,#416,#105,.T.);+#537=EDGE_CURVE('',#418,#419,#16,.T.);+#538=EDGE_CURVE('',#420,#418,#106,.T.);+#539=EDGE_CURVE('',#420,#421,#107,.T.);+#540=EDGE_CURVE('',#419,#421,#108,.T.);+#541=EDGE_CURVE('',#422,#423,#17,.T.);+#542=EDGE_CURVE('',#424,#422,#109,.T.);+#543=EDGE_CURVE('',#424,#425,#110,.T.);+#544=EDGE_CURVE('',#423,#425,#111,.T.);+#545=EDGE_CURVE('',#426,#427,#18,.T.);+#546=EDGE_CURVE('',#428,#426,#112,.T.);+#547=EDGE_CURVE('',#428,#429,#113,.T.);+#548=EDGE_CURVE('',#427,#429,#114,.T.);+#549=EDGE_CURVE('',#430,#431,#19,.T.);+#550=EDGE_CURVE('',#432,#430,#115,.T.);+#551=EDGE_CURVE('',#432,#433,#116,.T.);+#552=EDGE_CURVE('',#434,#433,#117,.T.);+#553=EDGE_CURVE('',#431,#434,#20,.T.);+#554=EDGE_CURVE('',#434,#426,#339,.T.);+#555=EDGE_CURVE('',#428,#433,#340,.T.);+#556=EDGE_CURVE('',#427,#418,#341,.T.);+#557=EDGE_CURVE('',#420,#429,#342,.T.);+#558=EDGE_CURVE('',#432,#425,#343,.T.);+#559=EDGE_CURVE('',#423,#430,#344,.T.);+#560=EDGE_CURVE('',#419,#422,#345,.T.);+#561=EDGE_CURVE('',#431,#435,#118,.T.);+#562=EDGE_CURVE('',#435,#435,#346,.T.);+#563=EDGE_CURVE('',#436,#436,#347,.T.);+#564=EDGE_CURVE('',#424,#421,#348,.T.);+#565=EDGE_CURVE('',#437,#437,#349,.T.);+#566=EDGE_CURVE('',#437,#438,#119,.T.);+#567=EDGE_CURVE('',#439,#439,#350,.T.);+#568=EDGE_CURVE('',#439,#440,#120,.T.);+#569=EDGE_CURVE('',#440,#440,#351,.T.);+#570=EDGE_CURVE('',#440,#437,#121,.T.);+#571=EDGE_CURVE('',#441,#441,#352,.T.);+#572=EDGE_CURVE('',#442,#442,#353,.T.);+#573=EDGE_CURVE('',#442,#443,#354,.T.);+#574=EDGE_CURVE('',#436,#442,#122,.T.);+#575=EDGE_CURVE('',#444,#444,#355,.T.);+#576=EDGE_CURVE('',#445,#445,#356,.T.);+#577=EDGE_CURVE('',#445,#444,#123,.T.);+#578=EDGE_CURVE('',#446,#446,#357,.T.);+#579=EDGE_CURVE('',#447,#447,#358,.T.);+#580=EDGE_CURVE('',#447,#446,#124,.T.);+#581=EDGE_CURVE('',#448,#448,#359,.T.);+#582=EDGE_CURVE('',#441,#448,#125,.T.);+#583=ORIENTED_EDGE('',*,*,#449,.T.);+#584=ORIENTED_EDGE('',*,*,#450,.T.);+#585=ORIENTED_EDGE('',*,*,#451,.F.);+#586=ORIENTED_EDGE('',*,*,#452,.T.);+#587=ORIENTED_EDGE('',*,*,#453,.T.);+#588=ORIENTED_EDGE('',*,*,#449,.F.);+#589=ORIENTED_EDGE('',*,*,#453,.F.);+#590=ORIENTED_EDGE('',*,*,#454,.F.);+#591=ORIENTED_EDGE('',*,*,#455,.F.);+#592=ORIENTED_EDGE('',*,*,#456,.T.);+#593=ORIENTED_EDGE('',*,*,#457,.T.);+#594=ORIENTED_EDGE('',*,*,#458,.F.);+#595=ORIENTED_EDGE('',*,*,#459,.T.);+#596=ORIENTED_EDGE('',*,*,#460,.F.);+#597=ORIENTED_EDGE('',*,*,#451,.T.);+#598=ORIENTED_EDGE('',*,*,#461,.T.);+#599=ORIENTED_EDGE('',*,*,#462,.T.);+#600=ORIENTED_EDGE('',*,*,#458,.T.);+#601=ORIENTED_EDGE('',*,*,#463,.T.);+#602=ORIENTED_EDGE('',*,*,#464,.F.);+#603=ORIENTED_EDGE('',*,*,#465,.F.);+#604=ORIENTED_EDGE('',*,*,#466,.T.);+#605=ORIENTED_EDGE('',*,*,#467,.T.);+#606=ORIENTED_EDGE('',*,*,#468,.F.);+#607=ORIENTED_EDGE('',*,*,#469,.F.);+#608=ORIENTED_EDGE('',*,*,#470,.T.);+#609=ORIENTED_EDGE('',*,*,#471,.T.);+#610=ORIENTED_EDGE('',*,*,#472,.T.);+#611=ORIENTED_EDGE('',*,*,#473,.T.);+#612=ORIENTED_EDGE('',*,*,#474,.F.);+#613=ORIENTED_EDGE('',*,*,#475,.F.);+#614=ORIENTED_EDGE('',*,*,#476,.T.);+#615=ORIENTED_EDGE('',*,*,#477,.T.);+#616=ORIENTED_EDGE('',*,*,#478,.F.);+#617=ORIENTED_EDGE('',*,*,#479,.F.);+#618=ORIENTED_EDGE('',*,*,#454,.T.);+#619=ORIENTED_EDGE('',*,*,#480,.T.);+#620=ORIENTED_EDGE('',*,*,#481,.T.);+#621=ORIENTED_EDGE('',*,*,#471,.F.);+#622=ORIENTED_EDGE('',*,*,#482,.F.);+#623=ORIENTED_EDGE('',*,*,#459,.F.);+#624=ORIENTED_EDGE('',*,*,#466,.F.);+#625=ORIENTED_EDGE('',*,*,#483,.F.);+#626=ORIENTED_EDGE('',*,*,#484,.F.);+#627=ORIENTED_EDGE('',*,*,#485,.F.);+#628=ORIENTED_EDGE('',*,*,#486,.F.);+#629=ORIENTED_EDGE('',*,*,#487,.F.);+#630=ORIENTED_EDGE('',*,*,#481,.F.);+#631=ORIENTED_EDGE('',*,*,#488,.F.);+#632=ORIENTED_EDGE('',*,*,#489,.T.);+#633=ORIENTED_EDGE('',*,*,#467,.F.);+#634=ORIENTED_EDGE('',*,*,#490,.F.);+#635=ORIENTED_EDGE('',*,*,#491,.T.);+#636=ORIENTED_EDGE('',*,*,#492,.F.);+#637=ORIENTED_EDGE('',*,*,#474,.T.);+#638=ORIENTED_EDGE('',*,*,#489,.F.);+#639=ORIENTED_EDGE('',*,*,#493,.T.);+#640=ORIENTED_EDGE('',*,*,#494,.F.);+#641=ORIENTED_EDGE('',*,*,#468,.T.);+#642=ORIENTED_EDGE('',*,*,#495,.F.);+#643=ORIENTED_EDGE('',*,*,#478,.T.);+#644=ORIENTED_EDGE('',*,*,#496,.F.);+#645=ORIENTED_EDGE('',*,*,#497,.T.);+#646=ORIENTED_EDGE('',*,*,#498,.F.);+#647=ORIENTED_EDGE('',*,*,#499,.T.);+#648=ORIENTED_EDGE('',*,*,#500,.F.);+#649=ORIENTED_EDGE('',*,*,#464,.T.);+#650=ORIENTED_EDGE('',*,*,#501,.T.);+#651=ORIENTED_EDGE('',*,*,#502,.F.);+#652=ORIENTED_EDGE('',*,*,#490,.T.);+#653=ORIENTED_EDGE('',*,*,#473,.F.);+#654=ORIENTED_EDGE('',*,*,#503,.F.);+#655=ORIENTED_EDGE('',*,*,#504,.T.);+#656=ORIENTED_EDGE('',*,*,#505,.F.);+#657=ORIENTED_EDGE('',*,*,#506,.F.);+#658=ORIENTED_EDGE('',*,*,#507,.F.);+#659=ORIENTED_EDGE('',*,*,#501,.F.);+#660=ORIENTED_EDGE('',*,*,#508,.F.);+#661=ORIENTED_EDGE('',*,*,#504,.F.);+#662=ORIENTED_EDGE('',*,*,#509,.T.);+#663=ORIENTED_EDGE('',*,*,#469,.T.);+#664=ORIENTED_EDGE('',*,*,#494,.T.);+#665=ORIENTED_EDGE('',*,*,#510,.F.);+#666=ORIENTED_EDGE('',*,*,#510,.T.);+#667=ORIENTED_EDGE('',*,*,#493,.F.);+#668=ORIENTED_EDGE('',*,*,#488,.T.);+#669=ORIENTED_EDGE('',*,*,#487,.T.);+#670=ORIENTED_EDGE('',*,*,#511,.T.);+#671=ORIENTED_EDGE('',*,*,#505,.T.);+#672=ORIENTED_EDGE('',*,*,#512,.T.);+#673=ORIENTED_EDGE('',*,*,#484,.T.);+#674=ORIENTED_EDGE('',*,*,#513,.T.);+#675=ORIENTED_EDGE('',*,*,#499,.F.);+#676=ORIENTED_EDGE('',*,*,#514,.T.);+#677=ORIENTED_EDGE('',*,*,#515,.T.);+#678=ORIENTED_EDGE('',*,*,#516,.T.);+#679=ORIENTED_EDGE('',*,*,#517,.T.);+#680=ORIENTED_EDGE('',*,*,#518,.T.);+#681=ORIENTED_EDGE('',*,*,#491,.F.);+#682=ORIENTED_EDGE('',*,*,#502,.T.);+#683=ORIENTED_EDGE('',*,*,#507,.T.);+#684=ORIENTED_EDGE('',*,*,#519,.T.);+#685=ORIENTED_EDGE('',*,*,#520,.T.);+#686=ORIENTED_EDGE('',*,*,#521,.T.);+#687=ORIENTED_EDGE('',*,*,#497,.F.);+#688=ORIENTED_EDGE('',*,*,#522,.T.);+#689=ORIENTED_EDGE('',*,*,#523,.T.);+#690=ORIENTED_EDGE('',*,*,#511,.F.);+#691=ORIENTED_EDGE('',*,*,#486,.T.);+#692=ORIENTED_EDGE('',*,*,#524,.F.);+#693=ORIENTED_EDGE('',*,*,#525,.T.);+#694=ORIENTED_EDGE('',*,*,#526,.T.);+#695=ORIENTED_EDGE('',*,*,#527,.T.);+#696=ORIENTED_EDGE('',*,*,#516,.F.);+#697=ORIENTED_EDGE('',*,*,#506,.T.);+#698=ORIENTED_EDGE('',*,*,#528,.T.);+#699=ORIENTED_EDGE('',*,*,#517,.F.);+#700=ORIENTED_EDGE('',*,*,#527,.F.);+#701=ORIENTED_EDGE('',*,*,#529,.F.);+#702=ORIENTED_EDGE('',*,*,#523,.F.);+#703=ORIENTED_EDGE('',*,*,#530,.F.);+#704=ORIENTED_EDGE('',*,*,#480,.F.);+#705=ORIENTED_EDGE('',*,*,#462,.F.);+#706=ORIENTED_EDGE('',*,*,#531,.F.);+#707=ORIENTED_EDGE('',*,*,#476,.F.);+#708=ORIENTED_EDGE('',*,*,#528,.F.);+#709=ORIENTED_EDGE('',*,*,#475,.T.);+#710=ORIENTED_EDGE('',*,*,#492,.T.);+#711=ORIENTED_EDGE('',*,*,#518,.F.);+#712=ORIENTED_EDGE('',*,*,#472,.F.);+#713=ORIENTED_EDGE('',*,*,#531,.T.);+#714=ORIENTED_EDGE('',*,*,#461,.F.);+#715=ORIENTED_EDGE('',*,*,#452,.F.);+#716=ORIENTED_EDGE('',*,*,#460,.T.);+#717=ORIENTED_EDGE('',*,*,#482,.T.);+#718=ORIENTED_EDGE('',*,*,#470,.F.);+#719=ORIENTED_EDGE('',*,*,#509,.F.);+#720=ORIENTED_EDGE('',*,*,#508,.T.);+#721=ORIENTED_EDGE('',*,*,#503,.T.);+#722=ORIENTED_EDGE('',*,*,#530,.T.);+#723=ORIENTED_EDGE('',*,*,#522,.F.);+#724=ORIENTED_EDGE('',*,*,#496,.T.);+#725=ORIENTED_EDGE('',*,*,#477,.F.);+#726=ORIENTED_EDGE('',*,*,#526,.F.);+#727=ORIENTED_EDGE('',*,*,#532,.F.);+#728=ORIENTED_EDGE('',*,*,#524,.T.);+#729=ORIENTED_EDGE('',*,*,#485,.T.);+#730=ORIENTED_EDGE('',*,*,#512,.F.);+#731=ORIENTED_EDGE('',*,*,#533,.T.);+#732=ORIENTED_EDGE('',*,*,#519,.F.);+#733=ORIENTED_EDGE('',*,*,#529,.T.);+#734=ORIENTED_EDGE('',*,*,#534,.T.);+#735=ORIENTED_EDGE('',*,*,#520,.F.);+#736=ORIENTED_EDGE('',*,*,#533,.F.);+#737=ORIENTED_EDGE('',*,*,#515,.F.);+#738=ORIENTED_EDGE('',*,*,#535,.F.);+#739=ORIENTED_EDGE('',*,*,#456,.F.);+#740=ORIENTED_EDGE('',*,*,#534,.F.);+#741=ORIENTED_EDGE('',*,*,#455,.T.);+#742=ORIENTED_EDGE('',*,*,#479,.T.);+#743=ORIENTED_EDGE('',*,*,#495,.T.);+#744=ORIENTED_EDGE('',*,*,#521,.F.);+#745=ORIENTED_EDGE('',*,*,#535,.T.);+#746=ORIENTED_EDGE('',*,*,#514,.F.);+#747=ORIENTED_EDGE('',*,*,#498,.T.);+#748=ORIENTED_EDGE('',*,*,#463,.F.);+#749=ORIENTED_EDGE('',*,*,#457,.F.);+#750=ORIENTED_EDGE('',*,*,#450,.F.);+#751=ORIENTED_EDGE('',*,*,#536,.T.);+#752=ORIENTED_EDGE('',*,*,#525,.F.);+#753=ORIENTED_EDGE('',*,*,#532,.T.);+#754=ORIENTED_EDGE('',*,*,#536,.F.);+#755=ORIENTED_EDGE('',*,*,#483,.T.);+#756=ORIENTED_EDGE('',*,*,#465,.T.);+#757=ORIENTED_EDGE('',*,*,#500,.T.);+#758=ORIENTED_EDGE('',*,*,#513,.F.);+#759=ORIENTED_EDGE('',*,*,#537,.F.);+#760=ORIENTED_EDGE('',*,*,#538,.F.);+#761=ORIENTED_EDGE('',*,*,#539,.T.);+#762=ORIENTED_EDGE('',*,*,#540,.F.);+#763=ORIENTED_EDGE('',*,*,#541,.F.);+#764=ORIENTED_EDGE('',*,*,#542,.F.);+#765=ORIENTED_EDGE('',*,*,#543,.T.);+#766=ORIENTED_EDGE('',*,*,#544,.F.);+#767=ORIENTED_EDGE('',*,*,#545,.F.);+#768=ORIENTED_EDGE('',*,*,#546,.F.);+#769=ORIENTED_EDGE('',*,*,#547,.T.);+#770=ORIENTED_EDGE('',*,*,#548,.F.);+#771=ORIENTED_EDGE('',*,*,#549,.F.);+#772=ORIENTED_EDGE('',*,*,#550,.F.);+#773=ORIENTED_EDGE('',*,*,#551,.T.);+#774=ORIENTED_EDGE('',*,*,#552,.F.);+#775=ORIENTED_EDGE('',*,*,#553,.F.);+#776=ORIENTED_EDGE('',*,*,#546,.T.);+#777=ORIENTED_EDGE('',*,*,#554,.F.);+#778=ORIENTED_EDGE('',*,*,#552,.T.);+#779=ORIENTED_EDGE('',*,*,#555,.F.);+#780=ORIENTED_EDGE('',*,*,#538,.T.);+#781=ORIENTED_EDGE('',*,*,#556,.F.);+#782=ORIENTED_EDGE('',*,*,#548,.T.);+#783=ORIENTED_EDGE('',*,*,#557,.F.);+#784=ORIENTED_EDGE('',*,*,#544,.T.);+#785=ORIENTED_EDGE('',*,*,#558,.F.);+#786=ORIENTED_EDGE('',*,*,#550,.T.);+#787=ORIENTED_EDGE('',*,*,#559,.F.);+#788=ORIENTED_EDGE('',*,*,#537,.T.);+#789=ORIENTED_EDGE('',*,*,#560,.T.);+#790=ORIENTED_EDGE('',*,*,#541,.T.);+#791=ORIENTED_EDGE('',*,*,#559,.T.);+#792=ORIENTED_EDGE('',*,*,#549,.T.);+#793=ORIENTED_EDGE('',*,*,#561,.T.);+#794=ORIENTED_EDGE('',*,*,#562,.T.);+#795=ORIENTED_EDGE('',*,*,#561,.F.);+#796=ORIENTED_EDGE('',*,*,#553,.T.);+#797=ORIENTED_EDGE('',*,*,#554,.T.);+#798=ORIENTED_EDGE('',*,*,#545,.T.);+#799=ORIENTED_EDGE('',*,*,#556,.T.);+#800=ORIENTED_EDGE('',*,*,#562,.F.);+#801=ORIENTED_EDGE('',*,*,#563,.T.);+#802=ORIENTED_EDGE('',*,*,#540,.T.);+#803=ORIENTED_EDGE('',*,*,#564,.F.);+#804=ORIENTED_EDGE('',*,*,#542,.T.);+#805=ORIENTED_EDGE('',*,*,#560,.F.);+#806=ORIENTED_EDGE('',*,*,#565,.F.);+#807=ORIENTED_EDGE('',*,*,#566,.T.);+#808=ORIENTED_EDGE('',*,*,#566,.F.);+#809=ORIENTED_EDGE('',*,*,#567,.F.);+#810=ORIENTED_EDGE('',*,*,#568,.T.);+#811=ORIENTED_EDGE('',*,*,#569,.T.);+#812=ORIENTED_EDGE('',*,*,#568,.F.);+#813=ORIENTED_EDGE('',*,*,#569,.F.);+#814=ORIENTED_EDGE('',*,*,#570,.T.);+#815=ORIENTED_EDGE('',*,*,#565,.T.);+#816=ORIENTED_EDGE('',*,*,#570,.F.);+#817=ORIENTED_EDGE('',*,*,#571,.T.);+#818=ORIENTED_EDGE('',*,*,#567,.T.);+#819=ORIENTED_EDGE('',*,*,#572,.F.);+#820=ORIENTED_EDGE('',*,*,#573,.T.);+#821=ORIENTED_EDGE('',*,*,#573,.F.);+#822=ORIENTED_EDGE('',*,*,#563,.F.);+#823=ORIENTED_EDGE('',*,*,#574,.T.);+#824=ORIENTED_EDGE('',*,*,#572,.T.);+#825=ORIENTED_EDGE('',*,*,#574,.F.);+#826=ORIENTED_EDGE('',*,*,#575,.F.);+#827=ORIENTED_EDGE('',*,*,#558,.T.);+#828=ORIENTED_EDGE('',*,*,#543,.F.);+#829=ORIENTED_EDGE('',*,*,#564,.T.);+#830=ORIENTED_EDGE('',*,*,#539,.F.);+#831=ORIENTED_EDGE('',*,*,#557,.T.);+#832=ORIENTED_EDGE('',*,*,#547,.F.);+#833=ORIENTED_EDGE('',*,*,#555,.T.);+#834=ORIENTED_EDGE('',*,*,#551,.F.);+#835=ORIENTED_EDGE('',*,*,#576,.F.);+#836=ORIENTED_EDGE('',*,*,#577,.T.);+#837=ORIENTED_EDGE('',*,*,#575,.T.);+#838=ORIENTED_EDGE('',*,*,#577,.F.);+#839=ORIENTED_EDGE('',*,*,#576,.T.);+#840=ORIENTED_EDGE('',*,*,#578,.F.);+#841=ORIENTED_EDGE('',*,*,#579,.F.);+#842=ORIENTED_EDGE('',*,*,#580,.T.);+#843=ORIENTED_EDGE('',*,*,#578,.T.);+#844=ORIENTED_EDGE('',*,*,#580,.F.);+#845=ORIENTED_EDGE('',*,*,#581,.F.);+#846=ORIENTED_EDGE('',*,*,#579,.T.);+#847=ORIENTED_EDGE('',*,*,#571,.F.);+#848=ORIENTED_EDGE('',*,*,#582,.T.);+#849=ORIENTED_EDGE('',*,*,#581,.T.);+#850=ORIENTED_EDGE('',*,*,#582,.F.);+#851=PLANE('',#942);+#852=PLANE('',#971);+#853=PLANE('',#972);+#854=PLANE('',#974);+#855=PLANE('',#976);+#856=PLANE('',#978);+#857=PLANE('',#980);+#858=PLANE('',#985);+#859=PLANE('',#1003);+#860=PLANE('',#1004);+#861=PLANE('',#1007);+#862=PLANE('',#1008);+#863=PLANE('',#1010);+#864=PLANE('',#1011);+#865=PLANE('',#1012);+#866=PLANE('',#1013);+#867=PLANE('',#1014);+#868=PLANE('',#1027);+#869=PLANE('',#1037);+#870=PLANE('',#1043);+#871=PLANE('',#1047);+#872=PLANE('',#1051);+#873=ADVANCED_FACE('',(#197,#191),#851,.F.);+#874=ADVANCED_FACE('',(#198),#180,.T.);+#875=ADVANCED_FACE('',(#199),#68,.F.);+#876=ADVANCED_FACE('',(#200),#41,.T.);+#877=ADVANCED_FACE('',(#201),#42,.T.);+#878=ADVANCED_FACE('',(#202),#43,.T.);+#879=ADVANCED_FACE('',(#203),#44,.T.);+#880=ADVANCED_FACE('',(#204),#45,.F.);+#881=ADVANCED_FACE('',(#205),#852,.F.);+#882=ADVANCED_FACE('',(#206),#853,.T.);+#883=ADVANCED_FACE('',(#207),#854,.T.);+#884=ADVANCED_FACE('',(#208),#855,.T.);+#885=ADVANCED_FACE('',(#209),#856,.T.);+#886=ADVANCED_FACE('',(#210),#857,.F.);+#887=ADVANCED_FACE('',(#211),#46,.F.);+#888=ADVANCED_FACE('',(#212),#858,.F.);+#889=ADVANCED_FACE('',(#213),#47,.F.);+#890=ADVANCED_FACE('',(#214),#48,.F.);+#891=ADVANCED_FACE('',(#215),#49,.F.);+#892=ADVANCED_FACE('',(#216),#50,.F.);+#893=ADVANCED_FACE('',(#217),#69,.T.);+#894=ADVANCED_FACE('',(#218),#51,.F.);+#895=ADVANCED_FACE('',(#219),#859,.F.);+#896=ADVANCED_FACE('',(#220),#70,.F.);+#897=ADVANCED_FACE('',(#221),#860,.F.);+#898=ADVANCED_FACE('',(#222),#71,.T.);+#899=ADVANCED_FACE('',(#223),#52,.F.);+#900=ADVANCED_FACE('',(#224),#861,.F.);+#901=ADVANCED_FACE('',(#225),#862,.F.);+#902=ADVANCED_FACE('',(#226),#181,.F.);+#903=ADVANCED_FACE('',(#227),#863,.F.);+#904=ADVANCED_FACE('',(#228),#864,.F.);+#905=ADVANCED_FACE('',(#229),#865,.F.);+#906=ADVANCED_FACE('',(#230),#866,.F.);+#907=ADVANCED_FACE('',(#231),#867,.F.);+#908=ADVANCED_FACE('',(#232),#182,.T.);+#909=ADVANCED_FACE('',(#233),#183,.T.);+#910=ADVANCED_FACE('',(#234),#184,.T.);+#911=ADVANCED_FACE('',(#235),#53,.T.);+#912=ADVANCED_FACE('',(#236,#192),#868,.T.);+#913=ADVANCED_FACE('',(#237),#185,.T.);+#914=ADVANCED_FACE('',(#238),#54,.F.);+#915=ADVANCED_FACE('',(#239),#55,.F.);+#916=ADVANCED_FACE('',(#240),#186,.F.);+#917=ADVANCED_FACE('',(#241,#193),#869,.T.);+#918=ADVANCED_FACE('',(#242),#15,.T.);+#919=ADVANCED_FACE('',(#243),#187,.T.);+#920=ADVANCED_FACE('',(#244,#194),#870,.T.);+#921=ADVANCED_FACE('',(#245),#188,.T.);+#922=ADVANCED_FACE('',(#246,#195),#871,.T.);+#923=ADVANCED_FACE('',(#247),#189,.T.);+#924=ADVANCED_FACE('',(#248,#196),#872,.T.);+#925=ADVANCED_FACE('',(#249),#190,.T.);+#926=CLOSED_SHELL('',(#873,#874,#875,#876,#877,#878,#879,#880,#881,#882,+#883,#884,#885,#886,#887,#888,#889,#890,#891,#892,#893,#894,#895,#896,#897,+#898,#899,#900,#901,#902,#903));+#927=CLOSED_SHELL('',(#904,#905,#906,#907,#908,#909,#910,#911,#912,#913,+#914,#915,#916,#917,#918,#919,#920,#921,#922,#923,#924,#925));+#928=DERIVED_UNIT_ELEMENT(#930,1.);+#929=DERIVED_UNIT_ELEMENT(#2255,-3.);+#930=(+MASS_UNIT()+NAMED_UNIT(*)+SI_UNIT(.KILO.,.GRAM.)+);+#931=DERIVED_UNIT((#928,#929));+#932=MEASURE_REPRESENTATION_ITEM('density measure',+POSITIVE_RATIO_MEASURE(8249.),#931);+#933=PROPERTY_DEFINITION_REPRESENTATION(#938,#935);+#934=PROPERTY_DEFINITION_REPRESENTATION(#939,#936);+#935=REPRESENTATION('material name',(#937),#2252);+#936=REPRESENTATION('density',(#932),#2252);+#937=DESCRIPTIVE_REPRESENTATION_ITEM('Beryllium Copper (Alloy 172)',+'Beryllium Copper (Alloy 172)');+#938=PROPERTY_DEFINITION('material property','material name',#2267);+#939=PROPERTY_DEFINITION('material property','density of part',#2267);+#940=AXIS2_PLACEMENT_3D('',#1339,#1055,#1056);+#941=AXIS2_PLACEMENT_3D('',#1340,#1057,#1058);+#942=AXIS2_PLACEMENT_3D('',#1341,#1059,#1060);+#943=AXIS2_PLACEMENT_3D('',#1343,#1061,#1062);+#944=AXIS2_PLACEMENT_3D('',#1345,#1063,#1064);+#945=AXIS2_PLACEMENT_3D('',#1346,#1065,#1066);+#946=AXIS2_PLACEMENT_3D('',#1349,#1067,#1068);+#947=AXIS2_PLACEMENT_3D('',#1350,#1069,#1070);+#948=AXIS2_PLACEMENT_3D('',#1369,#1072,#1073);+#949=AXIS2_PLACEMENT_3D('',#1371,#1074,#1075);+#950=AXIS2_PLACEMENT_3D('',#1447,#1076,#1077);+#951=AXIS2_PLACEMENT_3D('',#1449,#1078,#1079);+#952=AXIS2_PLACEMENT_3D('',#1485,#1080,#1081);+#953=AXIS2_PLACEMENT_3D('',#1487,#1082,#1083);+#954=AXIS2_PLACEMENT_3D('',#1522,#1084,#1085);+#955=AXIS2_PLACEMENT_3D('',#1526,#1087,#1088);+#956=AXIS2_PLACEMENT_3D('',#1529,#1090,#1091);+#957=AXIS2_PLACEMENT_3D('',#1530,#1092,#1093);+#958=AXIS2_PLACEMENT_3D('',#1535,#1095,#1096);+#959=AXIS2_PLACEMENT_3D('',#1539,#1098,#1099);+#960=AXIS2_PLACEMENT_3D('',#1540,#1100,#1101);+#961=AXIS2_PLACEMENT_3D('',#1541,#1102,#1103);+#962=AXIS2_PLACEMENT_3D('',#1544,#1104,#1105);+#963=AXIS2_PLACEMENT_3D('',#1548,#1107,#1108);+#964=AXIS2_PLACEMENT_3D('',#1551,#1110,#1111);+#965=AXIS2_PLACEMENT_3D('',#1552,#1112,#1113);+#966=AXIS2_PLACEMENT_3D('',#1557,#1115,#1116);+#967=AXIS2_PLACEMENT_3D('',#1559,#1118,#1119);+#968=AXIS2_PLACEMENT_3D('',#1560,#1120,#1121);+#969=AXIS2_PLACEMENT_3D('',#1600,#1124,#1125);+#970=AXIS2_PLACEMENT_3D('',#1665,#1126,#1127);+#971=AXIS2_PLACEMENT_3D('',#1666,#1128,#1129);+#972=AXIS2_PLACEMENT_3D('',#1670,#1132,#1133);+#973=AXIS2_PLACEMENT_3D('',#1674,#1135,#1136);+#974=AXIS2_PLACEMENT_3D('',#1676,#1138,#1139);+#975=AXIS2_PLACEMENT_3D('',#1678,#1140,#1141);+#976=AXIS2_PLACEMENT_3D('',#1680,#1143,#1144);+#977=AXIS2_PLACEMENT_3D('',#1685,#1147,#1148);+#978=AXIS2_PLACEMENT_3D('',#1686,#1149,#1150);+#979=AXIS2_PLACEMENT_3D('',#1690,#1152,#1153);+#980=AXIS2_PLACEMENT_3D('',#1692,#1155,#1156);+#981=AXIS2_PLACEMENT_3D('',#1697,#1159,#1160);+#982=AXIS2_PLACEMENT_3D('',#1698,#1161,#1162);+#983=AXIS2_PLACEMENT_3D('',#1703,#1164,#1165);+#984=AXIS2_PLACEMENT_3D('',#1790,#1166,#1167);+#985=AXIS2_PLACEMENT_3D('',#1864,#1168,#1169);+#986=AXIS2_PLACEMENT_3D('',#1865,#1170,#1171);+#987=AXIS2_PLACEMENT_3D('',#1867,#1173,#1174);+#988=AXIS2_PLACEMENT_3D('',#1868,#1175,#1176);+#989=AXIS2_PLACEMENT_3D('',#1869,#1177,#1178);+#990=AXIS2_PLACEMENT_3D('',#1871,#1179,#1180);+#991=AXIS2_PLACEMENT_3D('',#1875,#1183,#1184);+#992=AXIS2_PLACEMENT_3D('',#1876,#1185,#1186);+#993=AXIS2_PLACEMENT_3D('',#1878,#1187,#1188);+#994=AXIS2_PLACEMENT_3D('',#1880,#1189,#1190);+#995=AXIS2_PLACEMENT_3D('',#1882,#1192,#1193);+#996=AXIS2_PLACEMENT_3D('',#1885,#1194,#1195);+#997=AXIS2_PLACEMENT_3D('',#1887,#1196,#1197);+#998=AXIS2_PLACEMENT_3D('',#1891,#1200,#1201);+#999=AXIS2_PLACEMENT_3D('',#1908,#1202,#1203);+#1000=AXIS2_PLACEMENT_3D('',#1910,#1204,#1205);+#1001=AXIS2_PLACEMENT_3D('',#1912,#1206,#1207);+#1002=AXIS2_PLACEMENT_3D('',#1944,#1208,#1209);+#1003=AXIS2_PLACEMENT_3D('',#2012,#1212,#1213);+#1004=AXIS2_PLACEMENT_3D('',#2028,#1214,#1215);+#1005=AXIS2_PLACEMENT_3D('',#2044,#1216,#1217);+#1006=AXIS2_PLACEMENT_3D('',#2130,#1218,#1219);+#1007=AXIS2_PLACEMENT_3D('',#2133,#1222,#1223);+#1008=AXIS2_PLACEMENT_3D('',#2134,#1224,#1225);+#1009=AXIS2_PLACEMENT_3D('',#2135,#1226,#1227);+#1010=AXIS2_PLACEMENT_3D('',#2137,#1229,#1230);+#1011=AXIS2_PLACEMENT_3D('',#2138,#1231,#1232);+#1012=AXIS2_PLACEMENT_3D('',#2149,#1236,#1237);+#1013=AXIS2_PLACEMENT_3D('',#2160,#1241,#1242);+#1014=AXIS2_PLACEMENT_3D('',#2171,#1246,#1247);+#1015=AXIS2_PLACEMENT_3D('',#2186,#1251,#1252);+#1016=AXIS2_PLACEMENT_3D('',#2187,#1253,#1254);+#1017=AXIS2_PLACEMENT_3D('',#2188,#1255,#1256);+#1018=AXIS2_PLACEMENT_3D('',#2189,#1257,#1258);+#1019=AXIS2_PLACEMENT_3D('',#2190,#1259,#1260);+#1020=AXIS2_PLACEMENT_3D('',#2191,#1261,#1262);+#1021=AXIS2_PLACEMENT_3D('',#2192,#1263,#1264);+#1022=AXIS2_PLACEMENT_3D('',#2193,#1265,#1266);+#1023=AXIS2_PLACEMENT_3D('',#2194,#1267,#1268);+#1024=AXIS2_PLACEMENT_3D('',#2195,#1269,#1270);+#1025=AXIS2_PLACEMENT_3D('',#2196,#1271,#1272);+#1026=AXIS2_PLACEMENT_3D('',#2199,#1274,#1275);+#1027=AXIS2_PLACEMENT_3D('',#2200,#1276,#1277);+#1028=AXIS2_PLACEMENT_3D('',#2202,#1278,#1279);+#1029=AXIS2_PLACEMENT_3D('',#2203,#1280,#1281);+#1030=AXIS2_PLACEMENT_3D('',#2204,#1282,#1283);+#1031=AXIS2_PLACEMENT_3D('',#2205,#1284,#1285);+#1032=AXIS2_PLACEMENT_3D('',#2207,#1286,#1287);+#1033=AXIS2_PLACEMENT_3D('',#2210,#1289,#1290);+#1034=AXIS2_PLACEMENT_3D('',#2212,#1291,#1292);+#1035=AXIS2_PLACEMENT_3D('',#2215,#1294,#1295);+#1036=AXIS2_PLACEMENT_3D('',#2216,#1296,#1297);+#1037=AXIS2_PLACEMENT_3D('',#2218,#1299,#1300);+#1038=AXIS2_PLACEMENT_3D('',#2220,#1301,#1302);+#1039=AXIS2_PLACEMENT_3D('',#2221,#1303,#1304);+#1040=AXIS2_PLACEMENT_3D('',#2223,#1305,#1306);+#1041=AXIS2_PLACEMENT_3D('',#2225,#1307,#1308);+#1042=AXIS2_PLACEMENT_3D('',#2226,#1309,#1310);+#1043=AXIS2_PLACEMENT_3D('',#2228,#1312,#1313);+#1044=AXIS2_PLACEMENT_3D('',#2230,#1314,#1315);+#1045=AXIS2_PLACEMENT_3D('',#2231,#1316,#1317);+#1046=AXIS2_PLACEMENT_3D('',#2233,#1318,#1319);+#1047=AXIS2_PLACEMENT_3D('',#2235,#1321,#1322);+#1048=AXIS2_PLACEMENT_3D('',#2237,#1323,#1324);+#1049=AXIS2_PLACEMENT_3D('',#2238,#1325,#1326);+#1050=AXIS2_PLACEMENT_3D('',#2240,#1327,#1328);+#1051=AXIS2_PLACEMENT_3D('',#2242,#1330,#1331);+#1052=AXIS2_PLACEMENT_3D('',#2244,#1332,#1333);+#1053=AXIS2_PLACEMENT_3D('',#2245,#1334,#1335);+#1054=AXIS2_PLACEMENT_3D('',#2247,#1337,#1338);+#1055=DIRECTION('axis',(0.,0.,1.));+#1056=DIRECTION('refdir',(1.,0.,0.));+#1057=DIRECTION('axis',(0.,0.,1.));+#1058=DIRECTION('refdir',(1.,0.,0.));+#1059=DIRECTION('center_axis',(0.,0.,-1.));+#1060=DIRECTION('ref_axis',(1.,0.,0.));+#1061=DIRECTION('center_axis',(0.,0.,1.));+#1062=DIRECTION('ref_axis',(1.,0.,0.));+#1063=DIRECTION('center_axis',(0.,0.,-1.));+#1064=DIRECTION('ref_axis',(1.,0.,0.));+#1065=DIRECTION('center_axis',(0.,0.,-1.));+#1066=DIRECTION('ref_axis',(1.,0.,0.));+#1067=DIRECTION('center_axis',(0.,0.,-1.));+#1068=DIRECTION('ref_axis',(1.,0.,0.));+#1069=DIRECTION('center_axis',(0.,0.,1.));+#1070=DIRECTION('ref_axis',(1.,0.,0.));+#1071=DIRECTION('',(0.,0.,1.));+#1072=DIRECTION('center_axis',(0.,0.,-1.));+#1073=DIRECTION('ref_axis',(-0.258819045102493,-0.965925826289076,0.));+#1074=DIRECTION('center_axis',(-0.965925826289076,0.258819045102493,0.));+#1075=DIRECTION('ref_axis',(-0.25534951843075,-0.952977376471912,-0.1631892869336));+#1076=DIRECTION('center_axis',(-0.965925826289076,-0.258819045102493,0.));+#1077=DIRECTION('ref_axis',(0.25534951843075,-0.952977376471912,-0.1631892869336));+#1078=DIRECTION('center_axis',(0.,0.,-1.));+#1079=DIRECTION('ref_axis',(0.96737059980658,-0.253365590856095,0.));+#1080=DIRECTION('center_axis',(0.,1.,0.));+#1081=DIRECTION('ref_axis',(1.,0.,0.));+#1082=DIRECTION('center_axis',(0.,-1.,0.));+#1083=DIRECTION('ref_axis',(-1.,0.,0.));+#1084=DIRECTION('center_axis',(0.,0.,1.));+#1085=DIRECTION('ref_axis',(-1.,0.,0.));+#1086=DIRECTION('',(-0.0362924623015003,0.135445313241001,-0.990119904002107));+#1087=DIRECTION('center_axis',(0.,0.,-1.));+#1088=DIRECTION('ref_axis',(0.965925826289076,-0.258819045102493,0.));+#1089=DIRECTION('',(-0.135445313241001,0.0362924623015003,-0.990119904002107));+#1090=DIRECTION('center_axis',(-0.957600163102212,0.251556339988859,0.140432672259792));+#1091=DIRECTION('ref_axis',(-0.181219591627885,-0.904875323995765,0.385175424493544));+#1092=DIRECTION('center_axis',(0.,0.,1.));+#1093=DIRECTION('ref_axis',(-1.,0.,0.));+#1094=DIRECTION('',(-0.135445313241001,-0.0362924623015003,-0.990119904002107));+#1095=DIRECTION('center_axis',(0.,0.,-1.));+#1096=DIRECTION('ref_axis',(0.258819045102493,0.965925826289076,0.));+#1097=DIRECTION('',(-0.0362924623015003,-0.135445313241001,-0.990119904002107));+#1098=DIRECTION('center_axis',(0.,0.,-1.));+#1099=DIRECTION('ref_axis',(0.258819045102493,0.965925826289076,0.));+#1100=DIRECTION('center_axis',(-0.957600163102295,-0.25155633998854,0.140432672259801));+#1101=DIRECTION('ref_axis',(0.181219591614085,-0.904875323975133,-0.385175424548506));+#1102=DIRECTION('center_axis',(0.,0.,1.));+#1103=DIRECTION('ref_axis',(-1.,0.,0.));+#1104=DIRECTION('center_axis',(0.,0.,-1.));+#1105=DIRECTION('ref_axis',(-0.96737059980658,0.253365590856095,0.));+#1106=DIRECTION('',(0.0362924623015003,-0.135445313241001,-0.990119904002107));+#1107=DIRECTION('center_axis',(0.,0.,-1.));+#1108=DIRECTION('ref_axis',(-0.965925826289076,0.258819045102493,0.));+#1109=DIRECTION('',(0.135445313241001,-0.0362924623015003,-0.990119904002107));+#1110=DIRECTION('center_axis',(0.957603304947962,-0.251543639201056,0.140433998484091));+#1111=DIRECTION('ref_axis',(-0.181193095598003,-0.904856320073277,-0.3852325299492));+#1112=DIRECTION('center_axis',(0.,0.,1.));+#1113=DIRECTION('ref_axis',(-1.,0.,0.));+#1114=DIRECTION('',(0.135445313241001,0.0362924623015003,-0.990119904002107));+#1115=DIRECTION('center_axis',(0.,0.,-1.));+#1116=DIRECTION('ref_axis',(-0.258819045102493,-0.965925826289076,0.));+#1117=DIRECTION('',(0.0362924623015003,0.135445313241001,-0.990119904002107));+#1118=DIRECTION('center_axis',(0.957603304947995,0.251543639200925,0.1404339984841));+#1119=DIRECTION('ref_axis',(0.181193095549796,-0.904856319999574,0.385232530144991));+#1120=DIRECTION('center_axis',(0.968100345885965,-2.26300483551748E-12,+-0.250562807087314));+#1121=DIRECTION('ref_axis',(0.0467510353901437,0.98243899267131,0.180632129946286));+#1122=DIRECTION('',(0.933012701892336,0.250000000000174,-0.258819045101934));+#1123=DIRECTION('',(0.933012701892336,-0.250000000000174,-0.258819045101934));+#1124=DIRECTION('center_axis',(0.9568548906207,-0.254464714142449,-0.140272689970821));+#1125=DIRECTION('ref_axis',(0.182714717762633,0.902301409453812,-0.390470867556832));+#1126=DIRECTION('center_axis',(0.956854890620863,0.254464714141805,-0.140272689970877));+#1127=DIRECTION('ref_axis',(-0.182714719036956,0.902301411417719,0.390470862422332));+#1128=DIRECTION('center_axis',(-0.258819045102493,0.965925826289076,0.));+#1129=DIRECTION('ref_axis',(0.965925826289076,0.258819045102493,0.));+#1130=DIRECTION('',(0.135445313241001,0.0362924623015003,0.990119904002107));+#1131=DIRECTION('',(0.965925826289076,0.258819045102493,0.));+#1132=DIRECTION('center_axis',(0.,0.,-1.));+#1133=DIRECTION('ref_axis',(1.,0.,0.));+#1134=DIRECTION('',(-0.258819045102493,0.965925826289076,0.));+#1135=DIRECTION('center_axis',(0.,0.,1.));+#1136=DIRECTION('ref_axis',(-0.258819045102493,0.965925826289076,0.));+#1137=DIRECTION('',(0.965925826289076,-0.258819045102493,0.));+#1138=DIRECTION('center_axis',(0.,0.,-1.));+#1139=DIRECTION('ref_axis',(1.,0.,0.));+#1140=DIRECTION('center_axis',(0.,0.,1.));+#1141=DIRECTION('ref_axis',(0.965925826289076,0.258819045102493,0.));+#1142=DIRECTION('',(-0.258819045102493,-0.965925826289076,0.));+#1143=DIRECTION('center_axis',(0.,0.,-1.));+#1144=DIRECTION('ref_axis',(1.,0.,0.));+#1145=DIRECTION('',(0.258819045102493,0.965925826289076,0.));+#1146=DIRECTION('',(-0.965925826289076,-0.258819045102493,0.));+#1147=DIRECTION('center_axis',(0.,0.,1.));+#1148=DIRECTION('ref_axis',(-0.965925826289076,-0.258819045102493,0.));+#1149=DIRECTION('center_axis',(0.,0.,-1.));+#1150=DIRECTION('ref_axis',(1.,0.,0.));+#1151=DIRECTION('',(0.258819045102493,-0.965925826289076,0.));+#1152=DIRECTION('center_axis',(0.,0.,1.));+#1153=DIRECTION('ref_axis',(0.258819045102493,-0.965925826289076,0.));+#1154=DIRECTION('',(-0.965925826289076,0.258819045102493,0.));+#1155=DIRECTION('center_axis',(-0.965925826289076,-0.258819045102493,0.));+#1156=DIRECTION('ref_axis',(-0.258819045102493,0.965925826289076,0.));+#1157=DIRECTION('',(0.250000000000431,-0.93301270189216,0.258819045102318));+#1158=DIRECTION('',(-0.0362924623015003,0.135445313241001,0.990119904002107));+#1159=DIRECTION('center_axis',(0.965925826289076,0.258819045102493,0.));+#1160=DIRECTION('ref_axis',(-0.25534951843075,0.952977376471912,-0.1631892869336));+#1161=DIRECTION('center_axis',(8.99539455035338E-12,0.968100345888207,-0.250562807078652));+#1162=DIRECTION('ref_axis',(0.98243899266883,0.046751035381091,0.180632129962115));+#1163=DIRECTION('',(-0.250000000000431,-0.93301270189216,0.258819045102318));+#1164=DIRECTION('center_axis',(0.254457851799343,0.956856650686256,-0.140273132477836));+#1165=DIRECTION('ref_axis',(0.902386042589139,-0.182762172811296,0.390253018347848));+#1166=DIRECTION('center_axis',(-0.254457917399929,0.95685663386115,-0.140273128247718));+#1167=DIRECTION('ref_axis',(0.902385442836804,0.182761855001197,-0.390254553993603));+#1168=DIRECTION('center_axis',(0.965925826289076,-0.258819045102493,0.));+#1169=DIRECTION('ref_axis',(-0.258819045102493,-0.965925826289076,0.));+#1170=DIRECTION('center_axis',(0.965925826289076,-0.258819045102493,0.));+#1171=DIRECTION('ref_axis',(0.25534951843075,0.952977376471912,-0.1631892869336));+#1172=DIRECTION('',(-0.0362924623015003,-0.135445313241001,-0.990119904002107));+#1173=DIRECTION('center_axis',(0.,0.,1.));+#1174=DIRECTION('ref_axis',(-1.,0.,0.));+#1175=DIRECTION('center_axis',(0.,0.,1.));+#1176=DIRECTION('ref_axis',(0.966481459586918,0.256736417897305,0.));+#1177=DIRECTION('center_axis',(0.,0.,1.));+#1178=DIRECTION('ref_axis',(-1.,0.,0.));+#1179=DIRECTION('center_axis',(0.,0.,1.));+#1180=DIRECTION('ref_axis',(0.256736417897305,-0.966481459586918,0.));+#1181=DIRECTION('',(-0.135445313241001,0.0362924623015003,-0.990119904002107));+#1182=DIRECTION('',(0.0362924623015003,-0.135445313241001,0.990119904002107));+#1183=DIRECTION('center_axis',(0.254457836707408,-0.956856654557006,-0.140273133450992));+#1184=DIRECTION('ref_axis',(0.902386175738344,0.182762242790015,0.390252677693171));+#1185=DIRECTION('center_axis',(0.,0.,1.));+#1186=DIRECTION('ref_axis',(-1.,0.,0.));+#1187=DIRECTION('center_axis',(0.,0.,1.));+#1188=DIRECTION('ref_axis',(-0.256736417897305,0.966481459586918,0.));+#1189=DIRECTION('center_axis',(-0.956855105681015,0.254463875651683,-0.140272744039955));+#1190=DIRECTION('ref_axis',(0.182722083236711,0.902314165924266,0.390437941637378));+#1191=DIRECTION('',(0.135445313241001,-0.0362924623015003,-0.990119904002107));+#1192=DIRECTION('center_axis',(0.,0.,1.));+#1193=DIRECTION('ref_axis',(-1.,0.,0.));+#1194=DIRECTION('center_axis',(0.,0.,1.));+#1195=DIRECTION('ref_axis',(-0.966481459586918,-0.256736417897305,0.));+#1196=DIRECTION('center_axis',(-0.254458034360377,-0.956856603863353,-0.140273120705802));+#1197=DIRECTION('ref_axis',(0.902384375775184,-0.182761289834245,-0.390257286024175));+#1198=DIRECTION('',(0.0362924623015003,0.135445313241001,-0.990119904002107));+#1199=DIRECTION('',(-0.135445313241001,-0.0362924623015003,0.990119904002107));+#1200=DIRECTION('center_axis',(-0.956855105681172,-0.25446387565106,-0.140272744040013));+#1201=DIRECTION('ref_axis',(-0.182722081244468,0.902314162856912,-0.390437949658483));+#1202=DIRECTION('center_axis',(0.,1.,0.));+#1203=DIRECTION('ref_axis',(1.,0.,0.));+#1204=DIRECTION('center_axis',(0.,0.,1.));+#1205=DIRECTION('ref_axis',(1.,0.,0.));+#1206=DIRECTION('center_axis',(0.,-1.,0.));+#1207=DIRECTION('ref_axis',(-1.,0.,0.));+#1208=DIRECTION('center_axis',(-0.968100345887708,5.70544447831775E-12,+-0.250562807080581));+#1209=DIRECTION('ref_axis',(-0.046751035383631,0.982438992669549,0.180632129957545));+#1210=DIRECTION('',(0.933012701892336,-0.250000000000174,0.258819045101934));+#1211=DIRECTION('',(0.933012701892336,0.250000000000174,0.258819045101934));+#1212=DIRECTION('center_axis',(0.258819045102493,0.965925826289076,0.));+#1213=DIRECTION('ref_axis',(0.965925826289076,-0.258819045102493,0.));+#1214=DIRECTION('center_axis',(0.258819045102493,-0.965925826289076,0.));+#1215=DIRECTION('ref_axis',(-0.965925826289076,-0.258819045102493,0.));+#1216=DIRECTION('center_axis',(0.,0.,-1.));+#1217=DIRECTION('ref_axis',(1.,0.,0.));+#1218=DIRECTION('center_axis',(-2.57158511516849E-12,-0.968100345886812,+-0.250562807084044));+#1219=DIRECTION('ref_axis',(-0.982438992670233,-0.0467510353863553,0.18063212995312));+#1220=DIRECTION('',(0.250000000000431,0.93301270189216,0.258819045102318));+#1221=DIRECTION('',(-0.250000000000431,0.93301270189216,0.258819045102318));+#1222=DIRECTION('center_axis',(-0.965925826289076,0.258819045102493,0.));+#1223=DIRECTION('ref_axis',(0.258819045102493,0.965925826289076,0.));+#1224=DIRECTION('center_axis',(0.965925826289076,0.258819045102493,0.));+#1225=DIRECTION('ref_axis',(0.258819045102493,-0.965925826289076,0.));+#1226=DIRECTION('center_axis',(0.,0.,-1.));+#1227=DIRECTION('ref_axis',(1.,0.,0.));+#1228=DIRECTION('',(0.,0.,-1.));+#1229=DIRECTION('center_axis',(-0.258819045102493,-0.965925826289076,0.));+#1230=DIRECTION('ref_axis',(-0.965925826289076,0.258819045102493,0.));+#1231=DIRECTION('center_axis',(1.,6.93889390390727E-17,6.06546669921961E-16));+#1232=DIRECTION('ref_axis',(-6.93889390390727E-17,1.,-4.2839597923671E-32));+#1233=DIRECTION('',(6.17383671186386E-16,0.,-1.));+#1234=DIRECTION('',(-6.93889390390727E-17,1.,-4.2839597923671E-32));+#1235=DIRECTION('',(-6.17383671186386E-16,0.,1.));+#1236=DIRECTION('center_axis',(2.77555756156291E-16,-1.,1.6835051961427E-31));+#1237=DIRECTION('ref_axis',(1.,2.77555756156291E-16,6.17383671186386E-16));+#1238=DIRECTION('',(6.17383671186386E-16,0.,-1.));+#1239=DIRECTION('',(1.,2.77555756156291E-16,6.17383671186386E-16));+#1240=DIRECTION('',(-6.17383671186386E-16,0.,1.));+#1241=DIRECTION('center_axis',(-1.38777878078145E-16,1.,-8.4175259807135E-32));+#1242=DIRECTION('ref_axis',(-1.,-1.38777878078145E-16,-6.17383671186386E-16));+#1243=DIRECTION('',(6.17383671186386E-16,0.,-1.));+#1244=DIRECTION('',(-1.,-1.38777878078145E-16,-6.17383671186386E-16));+#1245=DIRECTION('',(-6.17383671186386E-16,0.,1.));+#1246=DIRECTION('center_axis',(-1.,-2.08166817117218E-16,-6.06546669921961E-16));+#1247=DIRECTION('ref_axis',(2.08166817117218E-16,-1.,1.28518793771013E-31));+#1248=DIRECTION('',(6.17383671186386E-16,0.,-1.));+#1249=DIRECTION('',(2.08166817117218E-16,-1.,1.28518793771013E-31));+#1250=DIRECTION('',(-6.17383671186386E-16,0.,1.));+#1251=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1252=DIRECTION('ref_axis',(1.,0.,6.93889390390722E-16));+#1253=DIRECTION('center_axis',(6.17383671186386E-16,0.,-1.));+#1254=DIRECTION('ref_axis',(-1.,0.,-6.93889390390722E-16));+#1255=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1256=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1257=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1258=DIRECTION('ref_axis',(1.,0.,6.93889390390722E-16));+#1259=DIRECTION('center_axis',(6.17383671186386E-16,0.,-1.));+#1260=DIRECTION('ref_axis',(-1.,0.,-6.93889390390722E-16));+#1261=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1262=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1263=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1264=DIRECTION('ref_axis',(1.,0.,6.93889390390722E-16));+#1265=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1266=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1267=DIRECTION('center_axis',(6.17383671186386E-16,0.,-1.));+#1268=DIRECTION('ref_axis',(-1.,0.,-6.93889390390722E-16));+#1269=DIRECTION('center_axis',(-6.17383671186384E-16,0.,1.));+#1270=DIRECTION('ref_axis',(-1.,0.,-6.61734812754117E-16));+#1271=DIRECTION('center_axis',(6.17383671186386E-16,0.,-1.));+#1272=DIRECTION('ref_axis',(-1.,0.,-6.93889390390722E-16));+#1273=DIRECTION('',(-0.707106781186548,-8.65956056235494E-17,-0.707106781186547));+#1274=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1275=DIRECTION('ref_axis',(-1.,0.,-6.17383671186386E-16));+#1276=DIRECTION('center_axis',(6.17383671186386E-16,0.,-1.));+#1277=DIRECTION('ref_axis',(-1.,0.,-6.17383671186386E-16));+#1278=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1279=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1280=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1281=DIRECTION('ref_axis',(1.,0.,6.93889390390722E-16));+#1282=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1283=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1284=DIRECTION('center_axis',(-4.64145255424366E-16,0.,1.));+#1285=DIRECTION('ref_axis',(1.,0.,4.64145255424366E-16));+#1286=DIRECTION('center_axis',(4.64145255424366E-16,0.,-1.));+#1287=DIRECTION('ref_axis',(1.,0.,4.64145255424366E-16));+#1288=DIRECTION('',(0.857167300702113,-1.04972719113862E-16,-0.515038074910054));+#1289=DIRECTION('center_axis',(-4.64145255424366E-16,0.,1.));+#1290=DIRECTION('ref_axis',(1.,0.,4.64145255424366E-16));+#1291=DIRECTION('center_axis',(6.17383671186386E-16,0.,-1.));+#1292=DIRECTION('ref_axis',(1.,0.,4.64145255424366E-16));+#1293=DIRECTION('',(0.707106781186548,-8.65956056235493E-17,-0.707106781186547));+#1294=DIRECTION('center_axis',(4.64145255424366E-16,0.,-1.));+#1295=DIRECTION('ref_axis',(1.,0.,4.64145255424366E-16));+#1296=DIRECTION('center_axis',(4.64145255424366E-16,0.,-1.));+#1297=DIRECTION('ref_axis',(1.,0.,4.64145255424366E-16));+#1298=DIRECTION('',(4.64145255424366E-16,0.,-1.));+#1299=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1300=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1301=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1302=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1303=DIRECTION('center_axis',(6.12323399573661E-17,0.,1.));+#1304=DIRECTION('ref_axis',(1.,0.,0.));+#1305=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1306=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1307=DIRECTION('center_axis',(1.22464679914735E-16,-1.,0.));+#1308=DIRECTION('ref_axis',(-1.,-1.22464679914735E-16,0.));+#1309=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1310=DIRECTION('ref_axis',(1.,0.,6.93889390390722E-16));+#1311=DIRECTION('',(6.17383671186386E-16,0.,-1.));+#1312=DIRECTION('center_axis',(6.17383671186386E-16,0.,-1.));+#1313=DIRECTION('ref_axis',(-1.,0.,-6.17383671186386E-16));+#1314=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1315=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1316=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1317=DIRECTION('ref_axis',(1.,0.,6.93889390390722E-16));+#1318=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1319=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1320=DIRECTION('',(6.17383671186386E-16,0.,-1.));+#1321=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1322=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1323=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1324=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1325=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1326=DIRECTION('ref_axis',(1.,0.,6.93889390390722E-16));+#1327=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1328=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1329=DIRECTION('',(6.17383671186386E-16,0.,-1.));+#1330=DIRECTION('center_axis',(6.17383671186386E-16,0.,-1.));+#1331=DIRECTION('ref_axis',(-1.,0.,-6.17383671186386E-16));+#1332=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1333=DIRECTION('ref_axis',(1.,0.,6.17383671186386E-16));+#1334=DIRECTION('center_axis',(-6.17383671186386E-16,0.,1.));+#1335=DIRECTION('ref_axis',(1.,0.,6.93889390390722E-16));+#1336=DIRECTION('',(6.17383671186386E-16,0.,-1.));+#1337=DIRECTION('',(0.,0.,0.999999999999998));+#1338=DIRECTION('',(1.,0.,0.));+#1339=CARTESIAN_POINT('',(0.,0.,0.));+#1340=CARTESIAN_POINT('',(0.,0.,0.));+#1341=CARTESIAN_POINT('Origin',(0.,0.,0.));+#1342=CARTESIAN_POINT('',(-0.435,0.,0.));+#1343=CARTESIAN_POINT('Origin',(0.,0.,0.));+#1344=CARTESIAN_POINT('',(-0.335,0.,0.));+#1345=CARTESIAN_POINT('Origin',(0.,0.,0.));+#1346=CARTESIAN_POINT('Origin',(0.,0.,0.0825));+#1347=CARTESIAN_POINT('',(0.435000000492065,8.79044664630808E-19,-0.4497766934534));+#1348=CARTESIAN_POINT('',(-0.435000000492065,-8.79044664630808E-19,-0.4497766934534));+#1349=CARTESIAN_POINT('Origin',(0.,0.,-0.4497766934534));+#1350=CARTESIAN_POINT('Origin',(0.,0.,-0.4497766934534));+#1351=CARTESIAN_POINT('',(-0.435,5.32721357629099E-17,0.0825));+#1352=CARTESIAN_POINT('Ctrl Pts',(-0.421594773337783,-3.49079421202853E-9,+-0.612965998035337));+#1353=CARTESIAN_POINT('Ctrl Pts',(-0.42159477358289,-0.42159477819506,-0.612965985033865));+#1354=CARTESIAN_POINT('Ctrl Pts',(1.12137642843461E-9,-0.42159477819506,+-0.612965980994025));+#1355=CARTESIAN_POINT('Ctrl Pts',(0.421594775825643,-0.42159477819506,-0.612965976954184));+#1356=CARTESIAN_POINT('Ctrl Pts',(0.421594775580536,-3.49079397317237E-9,+-0.612965989955655));+#1357=CARTESIAN_POINT('Ctrl Pts',(-0.435000000034728,-9.83700059612954E-10,+-0.531921933613031));+#1358=CARTESIAN_POINT('Ctrl Pts',(-0.435000000287629,-0.435000001616118,+-0.531921920198158));+#1359=CARTESIAN_POINT('Ctrl Pts',(3.44789210291141E-10,-0.435000001616118,+-0.531921916029865));+#1360=CARTESIAN_POINT('Ctrl Pts',(0.435000000977207,-0.435000001616118,+-0.531921911861572));+#1361=CARTESIAN_POINT('Ctrl Pts',(0.435000000724307,-9.83699813162012E-10,+-0.531921925276444));+#1362=CARTESIAN_POINT('Ctrl Pts',(-0.435000001180448,1.54955948644732E-9,+-0.4497766934534));+#1363=CARTESIAN_POINT('Ctrl Pts',(-0.435000001433348,-0.434999999441439,+-0.449776680038528));+#1364=CARTESIAN_POINT('Ctrl Pts',(-4.42349780916981E-10,-0.434999999441439,+-0.449776675870234));+#1365=CARTESIAN_POINT('Ctrl Pts',(0.435000000548649,-0.434999999441439,+-0.449776671701941));+#1366=CARTESIAN_POINT('Ctrl Pts',(0.435000000295749,1.54955973289826E-9,+-0.449776685116813));+#1367=CARTESIAN_POINT('',(-0.1100291276079,-0.4106342945452,-0.59));+#1368=CARTESIAN_POINT('',(-0.411248496150189,-0.107710755563898,-0.589999999945303));+#1369=CARTESIAN_POINT('Origin',(0.,0.,-0.59));+#1370=CARTESIAN_POINT('',(-0.1091167579479,-0.4072292846186,-0.612965980387));+#1371=CARTESIAN_POINT('Origin',(0.1462327604829,0.5457480918533,-0.4497766934534));+#1372=CARTESIAN_POINT('',(0.1091167579479,-0.4072292846186,-0.612965980387));+#1373=CARTESIAN_POINT('Ctrl Pts',(-0.109116757947898,-0.4072292846186,-0.612965980387));+#1374=CARTESIAN_POINT('Ctrl Pts',(-0.109350281160638,-0.408100805113327,+-0.607511153227363));+#1375=CARTESIAN_POINT('Ctrl Pts',(-0.109184347791537,-0.409018416160024,+-0.602137949891247));+#1376=CARTESIAN_POINT('Ctrl Pts',(-0.108631392112978,-0.40998574121446,+-0.596800830531668));+#1377=CARTESIAN_POINT('Ctrl Pts',(-0.108380768287011,-0.410424175460634,+-0.594381813374587));+#1378=CARTESIAN_POINT('Ctrl Pts',(-0.108049922796826,-0.410872829296172,+-0.59197018253976));+#1379=CARTESIAN_POINT('Ctrl Pts',(-0.107636510704227,-0.411331940263288,+-0.589562083557026));+#1380=CARTESIAN_POINT('Ctrl Pts',(-0.106753971966542,-0.412312035477314,+-0.584421351888818));+#1381=CARTESIAN_POINT('Ctrl Pts',(-0.105490936909929,-0.413344279962557,+-0.579297017524966));+#1382=CARTESIAN_POINT('Ctrl Pts',(-0.103866227590306,-0.41439629921771,+-0.574313018839391));+#1383=CARTESIAN_POINT('Ctrl Pts',(-0.103050702390725,-0.41492436180876,+-0.571811293470012));+#1384=CARTESIAN_POINT('Ctrl Pts',(-0.102144872529747,-0.415457436729791,+-0.569344810607068));+#1385=CARTESIAN_POINT('Ctrl Pts',(-0.101155873680074,-0.415991786263789,+-0.566927866607994));+#1386=CARTESIAN_POINT('Ctrl Pts',(-0.0987466141719913,-0.417293493233266,+-0.561040048525903));+#1387=CARTESIAN_POINT('Ctrl Pts',(-0.0958475632952568,-0.418598683082171,+-0.555441722784693));+#1388=CARTESIAN_POINT('Ctrl Pts',(-0.0924678464831636,-0.419891862068194,+-0.550122912635699));+#1389=CARTESIAN_POINT('Ctrl Pts',(-0.0916962386472333,-0.420187101904652,+-0.548908599183725));+#1390=CARTESIAN_POINT('Ctrl Pts',(-0.0908993849104523,-0.420481691250045,+-0.547708949476481));+#1391=CARTESIAN_POINT('Ctrl Pts',(-0.0900770736143311,-0.420775394237914,+-0.546524043732508));+#1392=CARTESIAN_POINT('Ctrl Pts',(-0.0856529594555881,-0.422355544666623,+-0.540149136439599));+#1393=CARTESIAN_POINT('Ctrl Pts',(-0.0804903751371729,-0.423912106474778,+-0.534207058227975));+#1394=CARTESIAN_POINT('Ctrl Pts',(-0.0746884788776826,-0.425360077392348,+-0.528866045924748));+#1395=CARTESIAN_POINT('Ctrl Pts',(-0.0704700825239476,-0.426412856512451,+-0.524982745393765));+#1396=CARTESIAN_POINT('Ctrl Pts',(-0.0659113653994954,-0.427409610569301,+-0.521417451535141));+#1397=CARTESIAN_POINT('Ctrl Pts',(-0.0611184059984039,-0.428313525699154,+-0.518249883445926));+#1398=CARTESIAN_POINT('Ctrl Pts',(-0.0593192268498737,-0.428652836992569,+-0.517060843026271));+#1399=CARTESIAN_POINT('Ctrl Pts',(-0.0574873843519088,-0.428979167538845,+-0.515927455185565));+#1400=CARTESIAN_POINT('Ctrl Pts',(-0.0556295628221114,-0.429290962421024,+-0.514852420752448));+#1401=CARTESIAN_POINT('Ctrl Pts',(-0.0488225603427207,-0.430433369537108,+-0.510913526765975));+#1402=CARTESIAN_POINT('Ctrl Pts',(-0.0416670623062016,-0.431377122489234,+-0.507754487716611));+#1403=CARTESIAN_POINT('Ctrl Pts',(-0.0342828714893517,-0.432095320015604,+-0.505375232836491));+#1404=CARTESIAN_POINT('Ctrl Pts',(-0.0268986806725018,-0.432813517541973,+-0.50299597795637));+#1405=CARTESIAN_POINT('Ctrl Pts',(-0.0192858374828511,-0.433306082430354,+-0.50139965881497));+#1406=CARTESIAN_POINT('Ctrl Pts',(-0.0115958284183545,-0.433552583907518,+-0.500600649889925));+#1407=CARTESIAN_POINT('Ctrl Pts',(-0.00774360260852997,-0.433676066122496,+-0.500200395120714));+#1408=CARTESIAN_POINT('Ctrl Pts',(-0.00382494856693413,-0.433737876172953,+-0.500000000000387));+#1409=CARTESIAN_POINT('Ctrl Pts',(-3.19698976399976E-17,-0.433737876322974,+-0.500000000000395));+#1410=CARTESIAN_POINT('Ctrl Pts',(0.00381060827812319,-0.433737876472433,+-0.500000000000403));+#1411=CARTESIAN_POINT('Ctrl Pts',(0.00771399441341037,-0.433676538948857,+-0.500198858118451));+#1412=CARTESIAN_POINT('Ctrl Pts',(0.0115518355573819,-0.433553990550268,+-0.500596087708366));+#1413=CARTESIAN_POINT('Ctrl Pts',(0.0192419606183529,-0.433308432572275,+-0.501392041764246));+#1414=CARTESIAN_POINT('Ctrl Pts',(0.0268550301203556,-0.432816828783024,+-0.502985194439097));+#1415=CARTESIAN_POINT('Ctrl Pts',(0.0342405079836257,-0.432099440946605,+-0.505361592263085));+#1416=CARTESIAN_POINT('Ctrl Pts',(0.0416259858468959,-0.431382053110187,+-0.507737990087072));+#1417=CARTESIAN_POINT('Ctrl Pts',(0.0487837900960855,-0.430438959935386,+-0.510894601847888));+#1418=CARTESIAN_POINT('Ctrl Pts',(0.0555932425533862,-0.429297048591832,+-0.514831413522586));+#1419=CARTESIAN_POINT('Ctrl Pts',(0.0605354096475284,-0.428468271725861,+-0.517688674254859));+#1420=CARTESIAN_POINT('Ctrl Pts',(0.0652919793650865,-0.427535836413629,+-0.52096181715331));+#1421=CARTESIAN_POINT('Ctrl Pts',(0.0697452688743458,-0.426532544726783,+-0.524586294857913));+#1422=CARTESIAN_POINT('Ctrl Pts',(0.071427842967876,-0.426153473795727,+-0.525955721273076));+#1423=CARTESIAN_POINT('Ctrl Pts',(0.0730674172440476,-0.425764399823899,+-0.527374869395907));+#1424=CARTESIAN_POINT('Ctrl Pts',(0.0746588772994185,-0.425367467882685,+-0.52883880560118));+#1425=CARTESIAN_POINT('Ctrl Pts',(0.0804624742207109,-0.423919971299775,+-0.534177359732785));+#1426=CARTESIAN_POINT('Ctrl Pts',(0.0856261181211008,-0.422363988342928,+-0.540116110772606));+#1427=CARTESIAN_POINT('Ctrl Pts',(0.0900519887041404,-0.420784354358351,+-0.546487913015413));+#1428=CARTESIAN_POINT('Ctrl Pts',(0.0935695952415433,-0.419528888179077,+-0.551552112883847));+#1429=CARTESIAN_POINT('Ctrl Pts',(0.096625369162184,-0.418257662028362,+-0.556883650829066));+#1430=CARTESIAN_POINT('Ctrl Pts',(0.0992229397767676,-0.416985080524721,+-0.562493072460409));+#1431=CARTESIAN_POINT('Ctrl Pts',(0.0998936458293468,-0.416656493454903,+-0.563941453889767));+#1432=CARTESIAN_POINT('Ctrl Pts',(0.100533567593754,-0.416327782921751,+-0.565408489690081));+#1433=CARTESIAN_POINT('Ctrl Pts',(0.101142244781152,-0.415999140758955,+-0.566894578286705));+#1434=CARTESIAN_POINT('Ctrl Pts',(0.103069308133959,-0.414958664327076,+-0.571599513731746));+#1435=CARTESIAN_POINT('Ctrl Pts',(0.104680216950115,-0.413921730415963,+-0.576498617113661));+#1436=CARTESIAN_POINT('Ctrl Pts',(0.105929985843235,-0.412918390359016,+-0.581475182203665));+#1437=CARTESIAN_POINT('Ctrl Pts',(0.106603784010251,-0.412377451394231,+-0.584158238610445));+#1438=CARTESIAN_POINT('Ctrl Pts',(0.107173451954694,-0.411846364678513,+-0.586863472649683));+#1439=CARTESIAN_POINT('Ctrl Pts',(0.107638075340547,-0.411330200190734,+-0.589571201903103));+#1440=CARTESIAN_POINT('Ctrl Pts',(0.108541739752711,-0.410326291449521,+-0.594837571695492));+#1441=CARTESIAN_POINT('Ctrl Pts',(0.109052712177428,-0.409373748779828,+-0.600113147393514));+#1442=CARTESIAN_POINT('Ctrl Pts',(0.109184812582585,-0.408468742842044,+-0.605443630040472));+#1443=CARTESIAN_POINT('Ctrl Pts',(0.109246611165165,-0.408045367273171,+-0.607937311052717));+#1444=CARTESIAN_POINT('Ctrl Pts',(0.109224761250832,-0.407632358434291,+-0.610443150447865));+#1445=CARTESIAN_POINT('Ctrl Pts',(0.109116757947463,-0.407229284618717,+-0.612965980387));+#1446=CARTESIAN_POINT('',(0.1100291276079,-0.4106342945452,-0.59));+#1447=CARTESIAN_POINT('Origin',(-0.1462327604829,0.5457480918533,-0.4497766934534));+#1448=CARTESIAN_POINT('',(0.4112484965242,-0.1077107556622,-0.59));+#1449=CARTESIAN_POINT('Origin',(0.,0.,-0.59));+#1450=CARTESIAN_POINT('',(0.4337380134347,0.,-0.5));+#1451=CARTESIAN_POINT('Ctrl Pts',(0.41124849652408,-0.107710755662755,-0.589999999999884));+#1452=CARTESIAN_POINT('Ctrl Pts',(0.412335365106771,-0.106754178724151,+-0.584287343180281));+#1453=CARTESIAN_POINT('Ctrl Pts',(0.413450266924349,-0.10536706227487,-0.578768617992668));+#1454=CARTESIAN_POINT('Ctrl Pts',(0.414588514085099,-0.103566593935282,+-0.573407235034667));+#1455=CARTESIAN_POINT('Ctrl Pts',(0.414919555429471,-0.103042955941542,+-0.571847960842464));+#1456=CARTESIAN_POINT('Ctrl Pts',(0.415252558744439,-0.102484019572242,+-0.570302044395535));+#1457=CARTESIAN_POINT('Ctrl Pts',(0.41558735785394,-0.10188941154278,-0.568768782073256));+#1458=CARTESIAN_POINT('Ctrl Pts',(0.416868551661492,-0.0996139929301025,+-0.562901364458009));+#1459=CARTESIAN_POINT('Ctrl Pts',(0.418179100460788,-0.0968126169870629,+-0.557221600045027));+#1460=CARTESIAN_POINT('Ctrl Pts',(0.419472455342409,-0.0935444334403834,+-0.551853719374602));+#1461=CARTESIAN_POINT('Ctrl Pts',(0.41967917214693,-0.0930220799324948,+-0.550995771447849));+#1462=CARTESIAN_POINT('Ctrl Pts',(0.419885461009904,-0.0924879001160726,+-0.550145747167932));+#1463=CARTESIAN_POINT('Ctrl Pts',(0.420091146313087,-0.0919422448308152,+-0.54930409797528));+#1464=CARTESIAN_POINT('Ctrl Pts',(0.421583732754105,-0.0879826149124715,+-0.543196543601639));+#1465=CARTESIAN_POINT('Ctrl Pts',(0.423042803560736,-0.0834187976160491,+-0.537528124715941));+#1466=CARTESIAN_POINT('Ctrl Pts',(0.424419963758391,-0.0783103373180915,+-0.532361072540811));+#1467=CARTESIAN_POINT('Ctrl Pts',(0.425647567685724,-0.0737566434667363,+-0.52775514974859));+#1468=CARTESIAN_POINT('Ctrl Pts',(0.426810703038626,-0.0687666605278043,+-0.523551336661771));+#1469=CARTESIAN_POINT('Ctrl Pts',(0.427862466337567,-0.0634558929164214,+-0.519838841448331));+#1470=CARTESIAN_POINT('Ctrl Pts',(0.427990600333191,-0.0628088938486997,+-0.519386556370582));+#1471=CARTESIAN_POINT('Ctrl Pts',(0.428117093408089,-0.0621571752992446,+-0.518941516824304));+#1472=CARTESIAN_POINT('Ctrl Pts',(0.428241871215412,-0.0615009786672012,+-0.518503843688651));+#1473=CARTESIAN_POINT('Ctrl Pts',(0.429390863694461,-0.0554585179831486,+-0.514473614660472));+#1474=CARTESIAN_POINT('Ctrl Pts',(0.430392859164001,-0.0490356625376673,+-0.511067306867878));+#1475=CARTESIAN_POINT('Ctrl Pts',(0.431211799817641,-0.0423348025940657,+-0.508318555234264));+#1476=CARTESIAN_POINT('Ctrl Pts',(0.432030740471281,-0.0356339426504594,+-0.505569803600648));+#1477=CARTESIAN_POINT('Ctrl Pts',(0.432666700118517,-0.0286530521637485,+-0.503483081870347));+#1478=CARTESIAN_POINT('Ctrl Pts',(0.433093524597873,-0.021573329802306,+-0.502094248777366));+#1479=CARTESIAN_POINT('Ctrl Pts',(0.433437698864698,-0.0158645235577617,+-0.500974349065335));+#1480=CARTESIAN_POINT('Ctrl Pts',(0.433645286200676,-0.0100929345254701,+-0.500301088787472));+#1481=CARTESIAN_POINT('Ctrl Pts',(0.43371340117873,-0.00423523222685219,+-0.500079922610463));+#1482=CARTESIAN_POINT('Ctrl Pts',(0.433729758349953,-0.00282856012323822,+-0.500026811633564));+#1483=CARTESIAN_POINT('Ctrl Pts',(0.4337380134347,-0.001416914414037,-0.499999999999675));+#1484=CARTESIAN_POINT('Ctrl Pts',(0.4337380134347,-2.52418620635553E-17,+-0.49999999999968));+#1485=CARTESIAN_POINT('Origin',(-0.565,0.,-0.4497766934534));+#1486=CARTESIAN_POINT('',(-0.4337380134347,0.,-0.5));+#1487=CARTESIAN_POINT('Origin',(0.565,0.,-0.4497766934534));+#1488=CARTESIAN_POINT('Ctrl Pts',(-0.4337380134347,1.02443375240448E-17,+-0.50000000000086));+#1489=CARTESIAN_POINT('Ctrl Pts',(-0.4337380134347,-0.00590035360668503,+-0.500000000000888));+#1490=CARTESIAN_POINT('Ctrl Pts',(-0.433595900266174,-0.0117089937891322,+-0.500460930105732));+#1491=CARTESIAN_POINT('Ctrl Pts',(-0.433317697622654,-0.017449358404652,+-0.501365210318225));+#1492=CARTESIAN_POINT('Ctrl Pts',(-0.433250889874865,-0.0188278529674483,+-0.501582364704689));+#1493=CARTESIAN_POINT('Ctrl Pts',(-0.433176174810409,-0.0202024136851877,+-0.501825315397726));+#1494=CARTESIAN_POINT('Ctrl Pts',(-0.43309352459788,-0.021573329802049,+-0.502094248778678));+#1495=CARTESIAN_POINT('Ctrl Pts',(-0.432666700118528,-0.028653052163398,+-0.503483081871689));+#1496=CARTESIAN_POINT('Ctrl Pts',(-0.432030740471299,-0.0356339426500078,+-0.505569803602007));+#1497=CARTESIAN_POINT('Ctrl Pts',(-0.431211799817669,-0.0423348025935119,+-0.508318555235622));+#1498=CARTESIAN_POINT('Ctrl Pts',(-0.430392859164039,-0.0490356625370114,+-0.511067306869236));+#1499=CARTESIAN_POINT('Ctrl Pts',(-0.429390863694512,-0.0554585179823892,+-0.514473614661813));+#1500=CARTESIAN_POINT('Ctrl Pts',(-0.428241871215479,-0.0615009786663426,+-0.518503843689957));+#1501=CARTESIAN_POINT('Ctrl Pts',(-0.427217656543755,-0.0668872427183308,+-0.522096399582496));+#1502=CARTESIAN_POINT('Ctrl Pts',(-0.426077030658468,-0.0719691296520287,+-0.526188533430569));+#1503=CARTESIAN_POINT('Ctrl Pts',(-0.424865704034675,-0.0766269248033127,+-0.530695646481541));+#1504=CARTESIAN_POINT('Ctrl Pts',(-0.424718130792243,-0.0771943736808975,+-0.531244738092239));+#1505=CARTESIAN_POINT('Ctrl Pts',(-0.42456952002882,-0.0777555708704115,+-0.531799943159116));+#1506=CARTESIAN_POINT('Ctrl Pts',(-0.424419963758513,-0.0783103373169548,+-0.532361072541968));+#1507=CARTESIAN_POINT('Ctrl Pts',(-0.423042803560878,-0.0834187976148281,+-0.537528124717033));+#1508=CARTESIAN_POINT('Ctrl Pts',(-0.42158373275427,-0.087982614911177,+-0.543196543602652));+#1509=CARTESIAN_POINT('Ctrl Pts',(-0.420091146313274,-0.0919422448294608,+-0.549304097976204));+#1510=CARTESIAN_POINT('Ctrl Pts',(-0.418804245175444,-0.0953562194625333,+-0.554570003157175));+#1511=CARTESIAN_POINT('Ctrl Pts',(-0.417493220642234,-0.0983172277711325,+-0.560165675685106));+#1512=CARTESIAN_POINT('Ctrl Pts',(-0.416203758307136,-0.100758524562346,+-0.565970220616314));+#1513=CARTESIAN_POINT('Ctrl Pts',(-0.41599766364802,-0.101148716827982,+-0.566897960555942));+#1514=CARTESIAN_POINT('Ctrl Pts',(-0.415792130955236,-0.101525731571307,+-0.567830993198341));+#1515=CARTESIAN_POINT('Ctrl Pts',(-0.415587357854201,-0.10188941154129,+-0.56876878207389));+#1516=CARTESIAN_POINT('Ctrl Pts',(-0.414436190055115,-0.103933902094499,+-0.574040726242413));+#1517=CARTESIAN_POINT('Ctrl Pts',(-0.413306528691451,-0.105560445530728,+-0.579461208407975));+#1518=CARTESIAN_POINT('Ctrl Pts',(-0.412203775352324,-0.106766751582098,+-0.585064060457944));+#1519=CARTESIAN_POINT('Ctrl Pts',(-0.411883056829601,-0.10711758678796,+-0.586693562150431));+#1520=CARTESIAN_POINT('Ctrl Pts',(-0.411564595214719,-0.107432550253235,+-0.588338563311708));+#1521=CARTESIAN_POINT('Ctrl Pts',(-0.411248496524411,-0.107710755661222,+-0.590000000000204));+#1522=CARTESIAN_POINT('Origin',(0.,0.,-1.12));+#1523=CARTESIAN_POINT('',(0.07117523740319,-0.2656296022295,-1.65));+#1524=CARTESIAN_POINT('',(0.1100291276079,-0.4106342945452,-0.59));+#1525=CARTESIAN_POINT('',(0.2656296022295,-0.07117523740319,-1.65));+#1526=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1527=CARTESIAN_POINT('',(0.4074829924761,-0.1091847387634,-0.613036359306));+#1528=CARTESIAN_POINT('',(0.4074829924761,-0.1091847387634,-0.613036359306));+#1529=CARTESIAN_POINT('Origin',(0.438078258287466,0.00940988922961147,-0.616847431334544));+#1530=CARTESIAN_POINT('Origin',(0.,0.,-1.12));+#1531=CARTESIAN_POINT('',(0.4074829924761,0.1091847387634,-0.613036359306));+#1532=CARTESIAN_POINT('',(0.2656296022295,0.07117523740319,-1.65));+#1533=CARTESIAN_POINT('',(0.4074829924761,0.1091847387634,-0.613036359306));+#1534=CARTESIAN_POINT('',(0.07117523740319,0.2656296022295,-1.65));+#1535=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1536=CARTESIAN_POINT('',(0.1100291276079,0.4106342945452,-0.59));+#1537=CARTESIAN_POINT('',(0.1100291276079,0.4106342945452,-0.59));+#1538=CARTESIAN_POINT('',(0.411248496150125,0.107710755564194,-0.589999999945241));+#1539=CARTESIAN_POINT('Origin',(0.,0.,-0.59));+#1540=CARTESIAN_POINT('Origin',(0.438078258286677,-0.00940988922638442,+-0.616847431333858));+#1541=CARTESIAN_POINT('Origin',(0.,0.,-1.12));+#1542=CARTESIAN_POINT('',(-0.4112484965242,0.1077107556622,-0.59));+#1543=CARTESIAN_POINT('',(-0.1100291276079,0.4106342945452,-0.59));+#1544=CARTESIAN_POINT('Origin',(0.,0.,-0.59));+#1545=CARTESIAN_POINT('',(-0.07117523740319,0.2656296022295,-1.65));+#1546=CARTESIAN_POINT('',(-0.1100291276079,0.4106342945452,-0.59));+#1547=CARTESIAN_POINT('',(-0.2656296022295,0.07117523740319,-1.65));+#1548=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1549=CARTESIAN_POINT('',(-0.4074829924761,0.1091847387634,-0.613036359306));+#1550=CARTESIAN_POINT('',(-0.4074829924761,0.1091847387634,-0.613036359306));+#1551=CARTESIAN_POINT('Origin',(-0.438076151933698,-0.00940803503759271,+-0.616847026987834));+#1552=CARTESIAN_POINT('Origin',(0.,0.,-1.12));+#1553=CARTESIAN_POINT('',(-0.4074829924761,-0.1091847387634,-0.613036359306));+#1554=CARTESIAN_POINT('',(-0.2656296022295,-0.07117523740319,-1.65));+#1555=CARTESIAN_POINT('',(-0.4074829924761,-0.1091847387634,-0.613036359306));+#1556=CARTESIAN_POINT('',(-0.07117523740319,-0.2656296022295,-1.65));+#1557=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1558=CARTESIAN_POINT('',(-0.1100291276079,-0.4106342945452,-0.59));+#1559=CARTESIAN_POINT('Origin',(-0.438076151931735,0.00940803502977114,+-0.616847026987093));+#1560=CARTESIAN_POINT('Origin',(0.319771950981689,-8.30759072645293E-14,+-0.582763071003387));+#1561=CARTESIAN_POINT('',(0.3134932330786,0.08400025863603,-0.5869634669142));+#1562=CARTESIAN_POINT('',(0.3134932330786,0.08400025863603,-0.5869634669142));+#1563=CARTESIAN_POINT('Ctrl Pts',(0.4337380134347,-2.52418620635553E-17,+-0.49999999999968));+#1564=CARTESIAN_POINT('Ctrl Pts',(0.4337380134347,0.00590035360673006,-0.499999999999698));+#1565=CARTESIAN_POINT('Ctrl Pts',(0.433595900266173,0.0117089937892233,+-0.500460930104539));+#1566=CARTESIAN_POINT('Ctrl Pts',(0.433317697622653,0.0174493584047889,+-0.501365210317032));+#1567=CARTESIAN_POINT('Ctrl Pts',(0.433250889874863,0.0188278529676167,+-0.501582364703499));+#1568=CARTESIAN_POINT('Ctrl Pts',(0.433176174810405,0.0202024136853875,+-0.50182531539654));+#1569=CARTESIAN_POINT('Ctrl Pts',(0.433093524597874,0.0215733298022802,+-0.502094248777498));+#1570=CARTESIAN_POINT('Ctrl Pts',(0.432666700118519,0.0286530521637058,+-0.50348308187052));+#1571=CARTESIAN_POINT('Ctrl Pts',(0.432030740471284,0.0356339426503914,+-0.50556980360086));+#1572=CARTESIAN_POINT('Ctrl Pts',(0.431211799817646,0.0423348025939663,+-0.508318555234508));+#1573=CARTESIAN_POINT('Ctrl Pts',(0.430392859164008,0.0490356625375365,+-0.511067306868154));+#1574=CARTESIAN_POINT('Ctrl Pts',(0.429390863694472,0.0554585179829801,+-0.514473614660774));+#1575=CARTESIAN_POINT('Ctrl Pts',(0.428241871215428,0.0615009786669916,+-0.518503843688969));+#1576=CARTESIAN_POINT('Ctrl Pts',(0.427217656543703,0.0668872427189912,+-0.522096399581527));+#1577=CARTESIAN_POINT('Ctrl Pts',(0.426077030658415,0.0719691296526989,+-0.526188533429619));+#1578=CARTESIAN_POINT('Ctrl Pts',(0.424865704034621,0.0766269248039919,+-0.530695646480608));+#1579=CARTESIAN_POINT('Ctrl Pts',(0.424718130792179,0.0771943736816169,+-0.531244738091346));+#1580=CARTESIAN_POINT('Ctrl Pts',(0.424569520028745,0.0777555708711703,+-0.531799943158264));+#1581=CARTESIAN_POINT('Ctrl Pts',(0.424419963758428,0.078310337317752,-0.532361072541157));+#1582=CARTESIAN_POINT('Ctrl Pts',(0.42304280356078,0.0834187976156653,-0.537528124716287));+#1583=CARTESIAN_POINT('Ctrl Pts',(0.421583732754159,0.0879826149120438,+-0.543196543601976));+#1584=CARTESIAN_POINT('Ctrl Pts',(0.420091146313151,0.0919422448303471,+-0.549304097975599));+#1585=CARTESIAN_POINT('Ctrl Pts',(0.418804245175317,0.0953562194634183,+-0.554570003156604));+#1586=CARTESIAN_POINT('Ctrl Pts',(0.417493220642103,0.0983172277720134,+-0.560165675684566));+#1587=CARTESIAN_POINT('Ctrl Pts',(0.416203758307001,0.100758524563222,-0.565970220615803));+#1588=CARTESIAN_POINT('Ctrl Pts',(0.415997663647878,0.101148716828872,-0.56689796055547));+#1589=CARTESIAN_POINT('Ctrl Pts',(0.415792130955085,0.101525731572209,-0.567830993197911));+#1590=CARTESIAN_POINT('Ctrl Pts',(0.415587357854041,0.101889411542205,-0.568768782073501));+#1591=CARTESIAN_POINT('Ctrl Pts',(0.414436190054948,0.10393390209541,-0.57404072624207));+#1592=CARTESIAN_POINT('Ctrl Pts',(0.413306528691278,0.105560445531632,-0.579461208407676));+#1593=CARTESIAN_POINT('Ctrl Pts',(0.412203775352145,0.106766751582991,-0.585064060457687));+#1594=CARTESIAN_POINT('Ctrl Pts',(0.411883056829417,0.107117586788852,-0.586693562150198));+#1595=CARTESIAN_POINT('Ctrl Pts',(0.411564595214531,0.107432550254126,-0.588338563311499));+#1596=CARTESIAN_POINT('Ctrl Pts',(0.411248496524219,0.107710755662111,-0.590000000000019));+#1597=CARTESIAN_POINT('',(0.3134932330786,-0.08400025863603,-0.5869634669142));+#1598=CARTESIAN_POINT('',(0.3134932330786,-0.08400025863603,-0.5869634669142));+#1599=CARTESIAN_POINT('',(0.3151772523208,-0.08372377758611,-0.5759776693453));+#1600=CARTESIAN_POINT('Origin',(0.337261589048336,0.00700086452435883,-0.589912773297936));+#1601=CARTESIAN_POINT('',(0.3335975848396,0.,-0.5));+#1602=CARTESIAN_POINT('Ctrl Pts',(0.3335975848396,-3.44294531470913E-18,+-0.499999999999708));+#1603=CARTESIAN_POINT('Ctrl Pts',(0.3335975848396,-0.00489738672783748,+-0.499999999999701));+#1604=CARTESIAN_POINT('Ctrl Pts',(0.333467936944575,-0.0097099577936956,+-0.500412423450436));+#1605=CARTESIAN_POINT('Ctrl Pts',(0.333215435045597,-0.0144573198281293,+-0.501219830155054));+#1606=CARTESIAN_POINT('Ctrl Pts',(0.333161212239585,-0.015476778658745,+-0.501393214423823));+#1607=CARTESIAN_POINT('Ctrl Pts',(0.333101276089344,-0.0164932328009472,+-0.501584998603484));+#1608=CARTESIAN_POINT('Ctrl Pts',(0.333035616046399,-0.0175068508243247,+-0.501795310686569));+#1609=CARTESIAN_POINT('Ctrl Pts',(0.332664193783638,-0.0232406317195058,+-0.502984992969448));+#1610=CARTESIAN_POINT('Ctrl Pts',(0.332111946297925,-0.0288818654186155,+-0.504770955125285));+#1611=CARTESIAN_POINT('Ctrl Pts',(0.331402441846759,-0.0342819607037802,+-0.507127204102421));+#1612=CARTESIAN_POINT('Ctrl Pts',(0.330692937395595,-0.0396820559889411,+-0.509483453079556));+#1613=CARTESIAN_POINT('Ctrl Pts',(0.329826121305491,-0.044841778642995,+-0.512409780913075));+#1614=CARTESIAN_POINT('Ctrl Pts',(0.328846826474069,-0.0496337800404193,+-0.515849012851062));+#1615=CARTESIAN_POINT('Ctrl Pts',(0.327867531642647,-0.0544257814378469,+-0.519288244789051));+#1616=CARTESIAN_POINT('Ctrl Pts',(0.326775720034315,-0.0588504153113541,+-0.523239836764691));+#1617=CARTESIAN_POINT('Ctrl Pts',(0.325624845085141,-0.0628064378067831,+-0.5276276829584));+#1618=CARTESIAN_POINT('Ctrl Pts',(0.324473970135969,-0.0667624603022093,+-0.532015529152106));+#1619=CARTESIAN_POINT('Ctrl Pts',(0.323264127199839,-0.0702497651847079,+-0.536838975202455));+#1620=CARTESIAN_POINT('Ctrl Pts',(0.322049860451666,-0.073188602986447,+-0.542008881797373));+#1621=CARTESIAN_POINT('Ctrl Pts',(0.321005863853219,-0.0757153432377297,+-0.546453839964516));+#1622=CARTESIAN_POINT('Ctrl Pts',(0.319959074809375,-0.0778347857194511,+-0.551157265417267));+#1623=CARTESIAN_POINT('Ctrl Pts',(0.318951187506329,-0.0794975048588054,+-0.556001802443122));+#1624=CARTESIAN_POINT('Ctrl Pts',(0.31878680658569,-0.0797686852795394,+-0.55679192000287));+#1625=CARTESIAN_POINT('Ctrl Pts',(0.318623469562294,-0.0800278236680716,+-0.55758575587909));+#1626=CARTESIAN_POINT('Ctrl Pts',(0.318461366050266,-0.080274832424007,+-0.558382767208169));+#1627=CARTESIAN_POINT('Ctrl Pts',(0.317558298056109,-0.0816509019426226,+-0.562822864854847));+#1628=CARTESIAN_POINT('Ctrl Pts',(0.316690987084049,-0.0826540312778184,+-0.567359782952275));+#1629=CARTESIAN_POINT('Ctrl Pts',(0.315863361522207,-0.0832839518711512,+-0.572025584906971));+#1630=CARTESIAN_POINT('Ctrl Pts',(0.315631534673147,-0.083460399425508,+-0.573332526373455));+#1631=CARTESIAN_POINT('Ctrl Pts',(0.315402807565895,-0.0836072561334219,+-0.574649634644172));+#1632=CARTESIAN_POINT('Ctrl Pts',(0.315177252320755,-0.0837237775862994,+-0.575977669345276));+#1633=CARTESIAN_POINT('',(0.3151772523208,0.08372377758611,-0.5759776693453));+#1634=CARTESIAN_POINT('Ctrl Pts',(0.315177252320807,0.0837237775860786,+-0.575977669345304));+#1635=CARTESIAN_POINT('Ctrl Pts',(0.315982932360349,0.083307564659159,-0.571233948799861));+#1636=CARTESIAN_POINT('Ctrl Pts',(0.316829077480533,0.0825083940599987,+-0.566629764112074));+#1637=CARTESIAN_POINT('Ctrl Pts',(0.317712220903149,0.0813412205777832,+-0.562131121131336));+#1638=CARTESIAN_POINT('Ctrl Pts',(0.317959367998803,0.0810145878600761,+-0.56087217890451));+#1639=CARTESIAN_POINT('Ctrl Pts',(0.318209404492153,0.0806588209017279,+-0.559621533727573));+#1640=CARTESIAN_POINT('Ctrl Pts',(0.318462217244647,0.0802735359630155,+-0.558378584134841));+#1641=CARTESIAN_POINT('Ctrl Pts',(0.319457549630515,0.0787566560845102,+-0.553485049408276));+#1642=CARTESIAN_POINT('Ctrl Pts',(0.320498977465152,0.0767785891006918,+-0.548712878370071));+#1643=CARTESIAN_POINT('Ctrl Pts',(0.321544013587405,0.0743809761375207,+-0.544182150640913));+#1644=CARTESIAN_POINT('Ctrl Pts',(0.321712916054747,0.0739934653744121,+-0.543449878257199));+#1645=CARTESIAN_POINT('Ctrl Pts',(0.321881921888001,0.0735950998208122,+-0.542723877701843));+#1646=CARTESIAN_POINT('Ctrl Pts',(0.322050864047508,0.0731861735039565,+-0.542004608270293));+#1647=CARTESIAN_POINT('Ctrl Pts',(0.323265087913289,0.0702471315577714,+-0.536835063188948));+#1648=CARTESIAN_POINT('Ctrl Pts',(0.32447482593786,0.0667598105994081,-0.532012175889439));+#1649=CARTESIAN_POINT('Ctrl Pts',(0.325625596190337,0.0628038562340689,+-0.52762481971362));+#1650=CARTESIAN_POINT('Ctrl Pts',(0.326776366442816,0.0588479018687269,+-0.523237463537798));+#1651=CARTESIAN_POINT('Ctrl Pts',(0.327868075100701,0.0544234085412131,+-0.519286302474599));+#1652=CARTESIAN_POINT('Ctrl Pts',(0.328847281066458,0.0496315555388093,+-0.515847416375792));+#1653=CARTESIAN_POINT('Ctrl Pts',(0.329826487032215,0.0448397025364088,+-0.512408530276987));+#1654=CARTESIAN_POINT('Ctrl Pts',(0.330693228950655,0.0396801295745825,+-0.509482470727628));+#1655=CARTESIAN_POINT('Ctrl Pts',(0.331402666665895,0.0342802504195448,+-0.507126457865922));+#1656=CARTESIAN_POINT('Ctrl Pts',(0.332112104381136,0.0288803712645033,+-0.504770445004214));+#1657=CARTESIAN_POINT('Ctrl Pts',(0.332664292384814,0.0232394241569825,+-0.502984677336385));+#1658=CARTESIAN_POINT('Ctrl Pts',(0.333035675132515,0.0175059361048368,+-0.501795120900751));+#1659=CARTESIAN_POINT('Ctrl Pts',(0.333341428972247,0.0127856414819754,+-0.500815776948929));+#1660=CARTESIAN_POINT('Ctrl Pts',(0.333524079573343,0.00800425093696496,+-0.50023426894306));+#1661=CARTESIAN_POINT('Ctrl Pts',(0.333579637568278,0.00314219531370886,+-0.500057197208565));+#1662=CARTESIAN_POINT('Ctrl Pts',(0.333591562882249,0.00209857342061351,+-0.500019189428916));+#1663=CARTESIAN_POINT('Ctrl Pts',(0.3335975848396,0.00105123145345343,-0.499999999999709));+#1664=CARTESIAN_POINT('Ctrl Pts',(0.3335975848396,-3.44294531470913E-18,+-0.499999999999708));+#1665=CARTESIAN_POINT('Origin',(0.337261589086643,-0.00700086467620174,+-0.589912773311632));+#1666=CARTESIAN_POINT('Origin',(0.2656296022295,0.07117523740319,-1.65));+#1667=CARTESIAN_POINT('',(0.1680731525122,0.04503506548499,-1.65));+#1668=CARTESIAN_POINT('',(0.1680731525122,0.04503506548499,-1.65));+#1669=CARTESIAN_POINT('',(0.1680731525122,0.04503506548499,-1.65));+#1670=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1671=CARTESIAN_POINT('',(-0.04503506548499,0.1680731525122,-1.65));+#1672=CARTESIAN_POINT('',(-0.04503506548499,0.1680731525122,-1.65));+#1673=CARTESIAN_POINT('',(-0.1680731525122,0.04503506548499,-1.65));+#1674=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1675=CARTESIAN_POINT('',(-0.2656296022295,0.07117523740319,-1.65));+#1676=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1677=CARTESIAN_POINT('',(0.04503506548499,0.1680731525122,-1.65));+#1678=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1679=CARTESIAN_POINT('',(0.07117523740319,0.2656296022295,-1.65));+#1680=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1681=CARTESIAN_POINT('',(-0.04503506548499,-0.1680731525122,-1.65));+#1682=CARTESIAN_POINT('',(-0.07117523740319,-0.2656296022295,-1.65));+#1683=CARTESIAN_POINT('',(-0.1680731525122,-0.04503506548499,-1.65));+#1684=CARTESIAN_POINT('',(-0.1680731525122,-0.04503506548499,-1.65));+#1685=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1686=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1687=CARTESIAN_POINT('',(0.04503506548499,-0.1680731525122,-1.65));+#1688=CARTESIAN_POINT('',(0.04503506548499,-0.1680731525122,-1.65));+#1689=CARTESIAN_POINT('',(0.1680731525122,-0.04503506548499,-1.65));+#1690=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1691=CARTESIAN_POINT('',(0.2656296022295,-0.07117523740319,-1.65));+#1692=CARTESIAN_POINT('Origin',(-0.07117523740319,0.2656296022295,-1.65));+#1693=CARTESIAN_POINT('',(-0.1091167579479,0.4072292846186,-0.612965980387));+#1694=CARTESIAN_POINT('',(-0.08400025863603,0.3134932330786,-0.5869634669142));+#1695=CARTESIAN_POINT('',(-0.1091167579479,0.4072292846186,-0.612965980387));+#1696=CARTESIAN_POINT('',(-0.04503506548499,0.1680731525122,-1.65));+#1697=CARTESIAN_POINT('Origin',(0.1462327604829,-0.5457480918533,-0.4497766934534));+#1698=CARTESIAN_POINT('Origin',(5.13911829758129E-13,0.401200496554915,+-0.60383832941405));+#1699=CARTESIAN_POINT('',(0.1091167579479,0.4072292846186,-0.612965980387));+#1700=CARTESIAN_POINT('',(0.08400025863603,0.3134932330786,-0.5869634669142));+#1701=CARTESIAN_POINT('',(0.1091167579479,0.4072292846186,-0.612965980387));+#1702=CARTESIAN_POINT('',(0.08372377758611,0.3151772523208,-0.5759776693453));+#1703=CARTESIAN_POINT('Origin',(-0.00700756595913425,0.337262594371428,+-0.589913312693652));+#1704=CARTESIAN_POINT('',(-0.08372377758611,0.3151772523208,-0.5759776693453));+#1705=CARTESIAN_POINT('Ctrl Pts',(-0.0837237775863238,0.31517725232075,+-0.575977669345273));+#1706=CARTESIAN_POINT('Ctrl Pts',(-0.0833113327840684,0.315975638250223,+-0.571276895401599));+#1707=CARTESIAN_POINT('Ctrl Pts',(-0.0825230902400074,0.316813484530678,+-0.566714996787816));+#1708=CARTESIAN_POINT('Ctrl Pts',(-0.0813735303111107,0.317687797688976,+-0.562255990679941));+#1709=CARTESIAN_POINT('Ctrl Pts',(-0.0809980749845387,0.31797335525246,+-0.560799644166519));+#1710=CARTESIAN_POINT('Ctrl Pts',(-0.0805836611437083,0.318262792863844,+-0.559354312006106));+#1711=CARTESIAN_POINT('Ctrl Pts',(-0.080129621328708,0.318555943250618,+-0.557918983885863));+#1712=CARTESIAN_POINT('Ctrl Pts',(-0.0786472399050521,0.319513041446269,+-0.553232822734341));+#1713=CARTESIAN_POINT('Ctrl Pts',(-0.0767386134609911,0.320513103970944,+-0.548654970226719));+#1714=CARTESIAN_POINT('Ctrl Pts',(-0.0744441546424222,0.321516271080799,+-0.544301820885805));+#1715=CARTESIAN_POINT('Ctrl Pts',(-0.0738841127241031,0.321761128660308,+-0.543239284444582));+#1716=CARTESIAN_POINT('Ctrl Pts',(-0.0733013145463806,0.322006191464224,+-0.542190056917317));+#1717=CARTESIAN_POINT('Ctrl Pts',(-0.072696780442275,0.322250927401882,+-0.541155647199318));+#1718=CARTESIAN_POINT('Ctrl Pts',(-0.0696155056498017,0.323498332089885,+-0.535883321672081));+#1719=CARTESIAN_POINT('Ctrl Pts',(-0.0659712429459372,0.324734823618324,+-0.530992338852893));+#1720=CARTESIAN_POINT('Ctrl Pts',(-0.0617978793291544,0.325914138928771,+-0.526529257994722));+#1721=CARTESIAN_POINT('Ctrl Pts',(-0.0583079344381202,0.326900332757189,+-0.522797039173288));+#1722=CARTESIAN_POINT('Ctrl Pts',(-0.0544438342087744,0.327847371036208,+-0.519369031417129));+#1723=CARTESIAN_POINT('Ctrl Pts',(-0.0503013782069154,0.328709149482636,+-0.516333191747931));+#1724=CARTESIAN_POINT('Ctrl Pts',(-0.0494901814428825,0.328877907312952,+-0.515738698212051));+#1725=CARTESIAN_POINT('Ctrl Pts',(-0.0486684257848271,0.32904342367623,+-0.515159135022842));+#1726=CARTESIAN_POINT('Ctrl Pts',(-0.0478369876905868,0.329205396013009,+-0.514594994011269));+#1727=CARTESIAN_POINT('Ctrl Pts',(-0.0430617484233418,0.330135659694878,+-0.511354935215078));+#1728=CARTESIAN_POINT('Ctrl Pts',(-0.0379624620523267,0.330947849687595,+-0.508625226976544));+#1729=CARTESIAN_POINT('Ctrl Pts',(-0.0326888097749409,0.331606324820217,+-0.506451366591713));+#1730=CARTESIAN_POINT('Ctrl Pts',(-0.0323552675167673,0.331647971343563,+-0.506313876620139));+#1731=CARTESIAN_POINT('Ctrl Pts',(-0.032021032986856,0.331689007119612,+-0.506178594145299));+#1732=CARTESIAN_POINT('Ctrl Pts',(-0.0316861458748877,0.331729424827186,+-0.506045524125155));+#1733=CARTESIAN_POINT('Ctrl Pts',(-0.0264079329621428,0.332366455212633,+-0.503948184832242));+#1734=CARTESIAN_POINT('Ctrl Pts',(-0.0209701854365,0.332848404642567,-0.502396914393403));+#1735=CARTESIAN_POINT('Ctrl Pts',(-0.0154011785895995,0.333163405219307,+-0.501385774727177));+#1736=CARTESIAN_POINT('Ctrl Pts',(-0.015030218327526,0.333184387900728,+-0.501318421131485));+#1737=CARTESIAN_POINT('Ctrl Pts',(-0.0146586701959157,0.333204625209552,+-0.501253481937854));+#1738=CARTESIAN_POINT('Ctrl Pts',(-0.0142865408610343,0.333224111631104,+-0.501190963145559));+#1739=CARTESIAN_POINT('Ctrl Pts',(-0.0110219332273635,0.333395061660003,+-0.500642499728568));+#1740=CARTESIAN_POINT('Ctrl Pts',(-0.00766076370280286,0.333510107488915,+-0.500275901559432));+#1741=CARTESIAN_POINT('Ctrl Pts',(-0.00432959778476578,0.333563070595375,+-0.500108629035027));+#1742=CARTESIAN_POINT('Ctrl Pts',(-0.00287705978997395,0.33358616488919,+-0.500035690699573));+#1743=CARTESIAN_POINT('Ctrl Pts',(-0.00143032372268609,0.333597623611267,+-0.500000000001246));+#1744=CARTESIAN_POINT('Ctrl Pts',(-4.23474191491434E-17,0.333597712147098,+-0.500000000001248));+#1745=CARTESIAN_POINT('Ctrl Pts',(0.00127664017257952,0.333597791170047,+-0.50000000000125));+#1746=CARTESIAN_POINT('Ctrl Pts',(0.00256728355619576,0.333588821040261,+-0.500028398573773));+#1747=CARTESIAN_POINT('Ctrl Pts',(0.00386503889744944,0.33357035186095,+-0.500086556013059));+#1748=CARTESIAN_POINT('Ctrl Pts',(0.008334416340219,0.333506745316039,-0.500286846113209));+#1749=CARTESIAN_POINT('Ctrl Pts',(0.0127941939934491,0.333332153703138,+-0.500839094638693));+#1750=CARTESIAN_POINT('Ctrl Pts',(0.0171213972212092,0.333059467387661,+-0.501716259588924));+#1751=CARTESIAN_POINT('Ctrl Pts',(0.0186867978887101,0.332960820913761,+-0.502033581084201));+#1752=CARTESIAN_POINT('Ctrl Pts',(0.0202349615821249,0.33284945897161,-0.502392952363184));+#1753=CARTESIAN_POINT('Ctrl Pts',(0.0217606118049948,0.332726389475778,+-0.502791556787527));+#1754=CARTESIAN_POINT('Ctrl Pts',(0.0268339602095553,0.332317138114639,+-0.504117063178164));+#1755=CARTESIAN_POINT('Ctrl Pts',(0.031658259000973,0.331775919681132,-0.505878978317662));+#1756=CARTESIAN_POINT('Ctrl Pts',(0.03614159392476,0.331150545189834,-0.507965490306588));+#1757=CARTESIAN_POINT('Ctrl Pts',(0.0367333426694265,0.331068002935806,+-0.508240885937556));+#1758=CARTESIAN_POINT('Ctrl Pts',(0.0373192154171622,0.33098400412168,-0.508521899577816));+#1759=CARTESIAN_POINT('Ctrl Pts',(0.0378990601104972,0.330898666503696,+-0.508808243912157));+#1760=CARTESIAN_POINT('Ctrl Pts',(0.0428720496772806,0.330166775552959,+-0.511264052171769));+#1761=CARTESIAN_POINT('Ctrl Pts',(0.0473963026943938,0.329335552213608,+-0.51411186040868));+#1762=CARTESIAN_POINT('Ctrl Pts',(0.0514357146595763,0.328469667754751,+-0.517179460546085));+#1763=CARTESIAN_POINT('Ctrl Pts',(0.0554751266247527,0.327603783295895,+-0.520247060683485));+#1764=CARTESIAN_POINT('Ctrl Pts',(0.0590305711095979,0.326703069400534,+-0.523534624941068));+#1765=CARTESIAN_POINT('Ctrl Pts',(0.0621104991631706,0.325825346817421,+-0.526865466368051));+#1766=CARTESIAN_POINT('Ctrl Pts',(0.0642828028044759,0.325206280420353,+-0.529214741687016));+#1767=CARTESIAN_POINT('Ctrl Pts',(0.066222549351971,0.324597097266371,-0.531581638040996));+#1768=CARTESIAN_POINT('Ctrl Pts',(0.0680142686977014,0.32398630359567,-0.534023438788317));+#1769=CARTESIAN_POINT('Ctrl Pts',(0.0687628786927333,0.32373110389161,-0.535043663551574));+#1770=CARTESIAN_POINT('Ctrl Pts',(0.0694852050164163,0.323475507565481,+-0.536077410578652));+#1771=CARTESIAN_POINT('Ctrl Pts',(0.0701854002153471,0.323218240397322,+-0.537130542152087));+#1772=CARTESIAN_POINT('Ctrl Pts',(0.0720490904064754,0.322533479492752,+-0.539933633327769));+#1773=CARTESIAN_POINT('Ctrl Pts',(0.0737570008558326,0.321838991983612,+-0.542869547564704));+#1774=CARTESIAN_POINT('Ctrl Pts',(0.0753242333748941,0.321123478652176,+-0.546020042051158));+#1775=CARTESIAN_POINT('Ctrl Pts',(0.0757550825759678,0.320926776284495,+-0.546886147122337));+#1776=CARTESIAN_POINT('Ctrl Pts',(0.0761750221607496,0.320728456938263,+-0.547768577223222));+#1777=CARTESIAN_POINT('Ctrl Pts',(0.0765836937365764,0.320528231597415,+-0.548669240624152));+#1778=CARTESIAN_POINT('Ctrl Pts',(0.0778445369043568,0.319910491661587,+-0.551447988554531));+#1779=CARTESIAN_POINT('Ctrl Pts',(0.0789953857101391,0.319277422894449,+-0.554403793550181));+#1780=CARTESIAN_POINT('Ctrl Pts',(0.0799722813187012,0.318656591965115,+-0.557426951712225));+#1781=CARTESIAN_POINT('Ctrl Pts',(0.080463802950968,0.318344223039435,-0.558948043240721));+#1782=CARTESIAN_POINT('Ctrl Pts',(0.0809120153593555,0.318035047242435,+-0.560485818444466));+#1783=CARTESIAN_POINT('Ctrl Pts',(0.0813134857293677,0.317733015006252,+-0.562024607131075));+#1784=CARTESIAN_POINT('Ctrl Pts',(0.0822696373082105,0.317013687700779,+-0.565689423620924));+#1785=CARTESIAN_POINT('Ctrl Pts',(0.0829657729657571,0.316329636811078,+-0.569360040407876));+#1786=CARTESIAN_POINT('Ctrl Pts',(0.0834225783649674,0.31567312954369,-0.573104383224986));+#1787=CARTESIAN_POINT('Ctrl Pts',(0.0835387864195512,0.315506118749826,+-0.574056917488423));+#1788=CARTESIAN_POINT('Ctrl Pts',(0.083639250041426,0.315340875675916,-0.575014280117207));+#1789=CARTESIAN_POINT('Ctrl Pts',(0.0837237775861321,0.315177252320795,+-0.575977669345297));+#1790=CARTESIAN_POINT('Origin',(0.00700751792596543,0.337262588819293,-0.589913308934869));+#1791=CARTESIAN_POINT('Ctrl Pts',(0.109116757948058,0.407229284618558,-0.612965980387));+#1792=CARTESIAN_POINT('Ctrl Pts',(0.109350437761226,0.408101389554135,-0.607507495225083));+#1793=CARTESIAN_POINT('Ctrl Pts',(0.109184107042277,0.409019598129749,-0.602131052666539));+#1794=CARTESIAN_POINT('Ctrl Pts',(0.108630396100019,0.409987481639581,-0.596791223188868));+#1795=CARTESIAN_POINT('Ctrl Pts',(0.108379696199731,0.410425703608639,-0.594373545271669));+#1796=CARTESIAN_POINT('Ctrl Pts',(0.108048870902961,0.410874115638989,-0.591963345882477));+#1797=CARTESIAN_POINT('Ctrl Pts',(0.107635601930886,0.41133295057794,-0.589556789561519));+#1798=CARTESIAN_POINT('Ctrl Pts',(0.106751857436914,0.412314134451738,-0.584410550569982));+#1799=CARTESIAN_POINT('Ctrl Pts',(0.105486889495109,0.41334746879785,-0.579281280503128));+#1800=CARTESIAN_POINT('Ctrl Pts',(0.103859669537717,0.414400545062016,-0.574292907612252));+#1801=CARTESIAN_POINT('Ctrl Pts',(0.103045280615941,0.414927587297104,-0.571796333254516));+#1802=CARTESIAN_POINT('Ctrl Pts',(0.102140972876719,0.415459604576563,-0.569334934333956));+#1803=CARTESIAN_POINT('Ctrl Pts',(0.101153832486935,0.415992888097741,-0.566922879002127));+#1804=CARTESIAN_POINT('Ctrl Pts',(0.0987437846644847,0.417294869864738,+-0.561033981373922));+#1805=CARTESIAN_POINT('Ctrl Pts',(0.0958437614954013,0.418600327094814,+-0.555434729195175));+#1806=CARTESIAN_POINT('Ctrl Pts',(0.0924629338731275,0.419893740802593,+-0.550115182354091));+#1807=CARTESIAN_POINT('Ctrl Pts',(0.091692113462856,0.420188635957352,-0.548902338714419));+#1808=CARTESIAN_POINT('Ctrl Pts',(0.0908961079770694,0.420482880673463,+-0.54770412916781));+#1809=CARTESIAN_POINT('Ctrl Pts',(0.0900747072236793,0.420776239756958,+-0.546520633971023));+#1810=CARTESIAN_POINT('Ctrl Pts',(0.0856506328526562,0.422356275321133,+-0.54014631455784));+#1811=CARTESIAN_POINT('Ctrl Pts',(0.0804882886921904,0.423912683694468,+-0.534204885945203));+#1812=CARTESIAN_POINT('Ctrl Pts',(0.0746866283011336,0.425360539551826,+-0.528864342363774));+#1813=CARTESIAN_POINT('Ctrl Pts',(0.0704676662403142,0.426413418989778,+-0.524980704019665));+#1814=CARTESIAN_POINT('Ctrl Pts',(0.0659082590674826,0.4274102728261,-0.521415098607128));+#1815=CARTESIAN_POINT('Ctrl Pts',(0.0611144663499047,0.428314270167142,+-0.518247279861325));+#1816=CARTESIAN_POINT('Ctrl Pts',(0.0593161264690999,0.428653395066009,+-0.517058906662615));+#1817=CARTESIAN_POINT('Ctrl Pts',(0.0574851450379426,0.428979553362164,+-0.515926123371802));+#1818=CARTESIAN_POINT('Ctrl Pts',(0.0556281928021243,0.429291192082261,+-0.514851628010358));+#1819=CARTESIAN_POINT('Ctrl Pts',(0.0488212062596705,0.430433558877103,+-0.510912875400467));+#1820=CARTESIAN_POINT('Ctrl Pts',(0.041665489199516,0.431377310049096,-0.507753849483488));+#1821=CARTESIAN_POINT('Ctrl Pts',(0.0342811699246263,0.432095485640747,+-0.505374684590758));+#1822=CARTESIAN_POINT('Ctrl Pts',(0.0268968506497366,0.432813661232397,+-0.502995519698027));+#1823=CARTESIAN_POINT('Ctrl Pts',(0.0192839788471603,0.433306183802944,+-0.501399334861433));+#1824=CARTESIAN_POINT('Ctrl Pts',(0.0115939515321883,0.433552644029669,+-0.500600454894252));+#1825=CARTESIAN_POINT('Ctrl Pts',(0.00774233795765498,0.433676085665954,+-0.500200329277009));+#1826=CARTESIAN_POINT('Ctrl Pts',(0.00382433573573818,0.433737875049254,+-0.500000000001352));+#1827=CARTESIAN_POINT('Ctrl Pts',(-3.2398094431426E-17,0.433737875150847,+-0.50000000000138));+#1828=CARTESIAN_POINT('Ctrl Pts',(-0.00381122938529555,0.433737875252091,+-0.500000000001408));+#1829=CARTESIAN_POINT('Ctrl Pts',(-0.00771527576773499,0.433676517661284,+-0.500198924534659));+#1830=CARTESIAN_POINT('Ctrl Pts',(-0.0115537389036249,0.433553929803453,+-0.500596284728585));+#1831=CARTESIAN_POINT('Ctrl Pts',(-0.0192438651793872,0.433308332523181,+-0.501392371589411));+#1832=CARTESIAN_POINT('Ctrl Pts',(-0.0268569294575289,0.432816690561329,+-0.502985655842928));+#1833=CARTESIAN_POINT('Ctrl Pts',(-0.0342424468967898,0.432099252456336,+-0.505362216162758));+#1834=CARTESIAN_POINT('Ctrl Pts',(-0.0416279643360508,0.431381814351344,+-0.507738776482587));+#1835=CARTESIAN_POINT('Ctrl Pts',(-0.0487858450791252,0.43043865900395,+-0.510895609930868));+#1836=CARTESIAN_POINT('Ctrl Pts',(-0.0555951124502964,0.429296735371906,+-0.514832494595315));+#1837=CARTESIAN_POINT('Ctrl Pts',(-0.0605382042378659,0.428467771987168,+-0.517690420594201));+#1838=CARTESIAN_POINT('Ctrl Pts',(-0.0652954910053779,0.427535129819114,+-0.520964316649503));+#1839=CARTESIAN_POINT('Ctrl Pts',(-0.0697493105590982,0.426531634471581,+-0.524589584474895));+#1840=CARTESIAN_POINT('Ctrl Pts',(-0.0714307701924751,0.4261527828143,-0.525958239274456));+#1841=CARTESIAN_POINT('Ctrl Pts',(-0.0730692707848219,0.425763944193504,+-0.527376543396521));+#1842=CARTESIAN_POINT('Ctrl Pts',(-0.0746597112527467,0.425367259736066,+-0.528839572752581));+#1843=CARTESIAN_POINT('Ctrl Pts',(-0.0804628813720279,0.423919844735877,+-0.534177847534909));+#1844=CARTESIAN_POINT('Ctrl Pts',(-0.0856260976633972,0.422363990033612,+-0.540116131586299));+#1845=CARTESIAN_POINT('Ctrl Pts',(-0.0900517608771974,0.420784435707682,+-0.546487585016092));+#1846=CARTESIAN_POINT('Ctrl Pts',(-0.0935728551913596,0.419527729201276,+-0.551556766144614));+#1847=CARTESIAN_POINT('Ctrl Pts',(-0.096631282453926,0.418255204505867,+-0.556893938139805));+#1848=CARTESIAN_POINT('Ctrl Pts',(-0.0992305159011457,0.416981365600478,+-0.562509437495612));+#1849=CARTESIAN_POINT('Ctrl Pts',(-0.0998982588041962,0.416654116468772,+-0.563952058913836));+#1850=CARTESIAN_POINT('Ctrl Pts',(-0.100535462258431,0.416326747832582,+-0.565413175876158));+#1851=CARTESIAN_POINT('Ctrl Pts',(-0.101141670763231,0.415999450390684,+-0.566893176961738));+#1852=CARTESIAN_POINT('Ctrl Pts',(-0.103070813256035,0.414957888951093,+-0.571602997033512));+#1853=CARTESIAN_POINT('Ctrl Pts',(-0.104683090770074,0.413919898974301,+-0.576507246781056));+#1854=CARTESIAN_POINT('Ctrl Pts',(-0.105933507647176,0.41291556368399,-0.58148920901895));+#1855=CARTESIAN_POINT('Ctrl Pts',(-0.106605515100266,0.412375807053342,+-0.584166648692216));+#1856=CARTESIAN_POINT('Ctrl Pts',(-0.107173846504871,0.411845857839403,+-0.586866197656916));+#1857=CARTESIAN_POINT('Ctrl Pts',(-0.107637587737012,0.411330742549284,+-0.589568359769692));+#1858=CARTESIAN_POINT('Ctrl Pts',(-0.10854180247563,0.410326357333714,-0.594837105870236));+#1859=CARTESIAN_POINT('Ctrl Pts',(-0.109053044235506,0.409373300007362,+-0.600115548652852));+#1860=CARTESIAN_POINT('Ctrl Pts',(-0.109184952909735,0.40846777989665,-0.605449300611543));+#1861=CARTESIAN_POINT('Ctrl Pts',(-0.109246576175376,0.408044751565484,+-0.607941048904321));+#1862=CARTESIAN_POINT('Ctrl Pts',(-0.109224681686036,0.407632061498369,+-0.610445008964787));+#1863=CARTESIAN_POINT('Ctrl Pts',(-0.109116757946487,0.407229284618979,+-0.612965980387));+#1864=CARTESIAN_POINT('Origin',(0.15535643361,0.5797981035151,-1.65));+#1865=CARTESIAN_POINT('Origin',(-0.1462327604829,-0.5457480918533,-0.4497766934534));+#1866=CARTESIAN_POINT('',(0.08400025863603,0.3134932330786,-0.5869634669142));+#1867=CARTESIAN_POINT('Origin',(0.,0.,-1.112988834673));+#1868=CARTESIAN_POINT('Origin',(0.,0.,-0.5759776693453));+#1869=CARTESIAN_POINT('Origin',(0.,0.,-1.112988834673));+#1870=CARTESIAN_POINT('',(0.08372377758611,-0.3151772523208,-0.5759776693453));+#1871=CARTESIAN_POINT('Origin',(0.,0.,-0.5759776693453));+#1872=CARTESIAN_POINT('',(0.3134932330786,-0.08400025863603,-0.5869634669142));+#1873=CARTESIAN_POINT('',(0.08400025863603,-0.3134932330786,-0.5869634669142));+#1874=CARTESIAN_POINT('',(0.04503506548499,-0.1680731525122,-1.65));+#1875=CARTESIAN_POINT('Origin',(-0.00700757663842628,-0.337262595554786,+-0.589913313526065));+#1876=CARTESIAN_POINT('Origin',(0.,0.,-1.112988834673));+#1877=CARTESIAN_POINT('',(-0.3151772523208,0.08372377758611,-0.5759776693453));+#1878=CARTESIAN_POINT('Origin',(0.,0.,-0.5759776693453));+#1879=CARTESIAN_POINT('',(-0.3134932330786,0.08400025863603,-0.5869634669142));+#1880=CARTESIAN_POINT('Origin',(-0.337261758629422,-0.00700186795583252,+-0.589912855265648));+#1881=CARTESIAN_POINT('',(-0.3134932330786,0.08400025863603,-0.5869634669142));+#1882=CARTESIAN_POINT('Origin',(0.,0.,-1.112988834673));+#1883=CARTESIAN_POINT('',(-0.3151772523208,-0.08372377758611,-0.5759776693453));+#1884=CARTESIAN_POINT('',(-0.08372377758611,-0.3151772523208,-0.5759776693453));+#1885=CARTESIAN_POINT('Origin',(0.,0.,-0.5759776693453));+#1886=CARTESIAN_POINT('',(-0.08400025863603,-0.3134932330786,-0.5869634669142));+#1887=CARTESIAN_POINT('Origin',(0.00700743245344285,-0.337262578962899,+-0.589913302244857));+#1888=CARTESIAN_POINT('',(-0.08400025863603,-0.3134932330786,-0.5869634669142));+#1889=CARTESIAN_POINT('',(-0.3134932330786,-0.08400025863603,-0.5869634669142));+#1890=CARTESIAN_POINT('',(-0.1680731525122,-0.04503506548499,-1.65));+#1891=CARTESIAN_POINT('Origin',(-0.337261758569529,0.00700186771921463,+-0.589912855244523));+#1892=CARTESIAN_POINT('Ctrl Pts',(0.326107911898371,1.07988145098768E-9,+-0.575977675627819));+#1893=CARTESIAN_POINT('Ctrl Pts',(0.32610791209425,0.326107913554983,-0.575977671012111));+#1894=CARTESIAN_POINT('Ctrl Pts',(-3.80851438377974E-10,0.326107913554983,+-0.575977669578031));+#1895=CARTESIAN_POINT('Ctrl Pts',(-0.326107912855953,0.326107913554983,+-0.575977668143951));+#1896=CARTESIAN_POINT('Ctrl Pts',(-0.326107912660074,1.07988133117757E-9,+-0.57597767275966));+#1897=CARTESIAN_POINT('Ctrl Pts',(0.335000000013656,1.85854373458481E-10,+-0.5131904516392));+#1898=CARTESIAN_POINT('Ctrl Pts',(0.335000000214875,0.33500000050547,-0.513190446897633));+#1899=CARTESIAN_POINT('Ctrl Pts',(-1.04740742565666E-10,0.33500000050547,+-0.51319044542445));+#1900=CARTESIAN_POINT('Ctrl Pts',(-0.335000000424357,0.33500000050547,-0.513190443951267));+#1901=CARTESIAN_POINT('Ctrl Pts',(-0.335000000223137,1.85854250381476E-10,+-0.513190448692833));+#1902=CARTESIAN_POINT('Ctrl Pts',(0.3350000004647,-7.11699545517685E-10,+-0.4497766934534));+#1903=CARTESIAN_POINT('Ctrl Pts',(0.335000000665919,0.334999999780095,-0.449776688711833));+#1904=CARTESIAN_POINT('Ctrl Pts',(1.74125175346648E-10,0.334999999780095,+-0.44977668723865));+#1905=CARTESIAN_POINT('Ctrl Pts',(-0.335000000317669,0.334999999780095,+-0.449776685765467));+#1906=CARTESIAN_POINT('Ctrl Pts',(-0.33500000011645,-7.1169966859469E-10,+-0.449776690507034));+#1907=CARTESIAN_POINT('',(0.335,0.,-0.4497766934534));+#1908=CARTESIAN_POINT('Origin',(-0.565,0.,-0.4497766934534));+#1909=CARTESIAN_POINT('',(-0.335,0.,-0.4497766934534));+#1910=CARTESIAN_POINT('Origin',(0.,0.,-0.4497766934534));+#1911=CARTESIAN_POINT('',(-0.3335975848396,0.,-0.5));+#1912=CARTESIAN_POINT('Origin',(0.565,0.,-0.4497766934534));+#1913=CARTESIAN_POINT('Ctrl Pts',(-0.3335975848396,-1.07856518136231E-17,+-0.500000000000804));+#1914=CARTESIAN_POINT('Ctrl Pts',(-0.3335975848396,0.00489738672778759,+-0.500000000000808));+#1915=CARTESIAN_POINT('Ctrl Pts',(-0.333467936944576,0.00970995779359471,+-0.500412423451544));+#1916=CARTESIAN_POINT('Ctrl Pts',(-0.333215435045599,0.0144573198279792,+-0.501219830156154));+#1917=CARTESIAN_POINT('Ctrl Pts',(-0.333161212239588,0.0154767786585698,+-0.501393214424919));+#1918=CARTESIAN_POINT('Ctrl Pts',(-0.333101276089348,0.0164932328007471,+-0.501584998604575));+#1919=CARTESIAN_POINT('Ctrl Pts',(-0.333035616046404,0.0175068508241,-0.501795310687654));+#1920=CARTESIAN_POINT('Ctrl Pts',(-0.332664193783648,0.0232406317192084,+-0.502984992970513));+#1921=CARTESIAN_POINT('Ctrl Pts',(-0.33211194629794,0.0288818654182494,+-0.504770955126313));+#1922=CARTESIAN_POINT('Ctrl Pts',(-0.331402441846782,0.0342819607033563,+-0.507127204103399));+#1923=CARTESIAN_POINT('Ctrl Pts',(-0.330692937395624,0.0396820559884595,+-0.509483453080484));+#1924=CARTESIAN_POINT('Ctrl Pts',(-0.329826121305529,0.0448417786424667,+-0.512409780913939));+#1925=CARTESIAN_POINT('Ctrl Pts',(-0.328846826474115,0.04963378003986,-0.515849012851854));+#1926=CARTESIAN_POINT('Ctrl Pts',(-0.327867531642701,0.0544257814372568,+-0.519288244789772));+#1927=CARTESIAN_POINT('Ctrl Pts',(-0.326775720034377,0.058850415310749,+-0.523239836765332));+#1928=CARTESIAN_POINT('Ctrl Pts',(-0.32562484508521,0.062806437806181,-0.527627682958961));+#1929=CARTESIAN_POINT('Ctrl Pts',(-0.324473970136043,0.0667624603016102,+-0.532015529152587));+#1930=CARTESIAN_POINT('Ctrl Pts',(-0.323264127199919,0.0702497651841301,+-0.536838975202855));+#1931=CARTESIAN_POINT('Ctrl Pts',(-0.322049860451746,0.0731886029859069,+-0.542008881797699));+#1932=CARTESIAN_POINT('Ctrl Pts',(-0.321005863853292,0.0757153432372426,+-0.546453839964815));+#1933=CARTESIAN_POINT('Ctrl Pts',(-0.319959074809438,0.0778347857190225,+-0.551157265417548));+#1934=CARTESIAN_POINT('Ctrl Pts',(-0.31895118750638,0.0794975048584361,+-0.556001802443396));+#1935=CARTESIAN_POINT('Ctrl Pts',(-0.318786806585747,0.079768685279164,+-0.556791920003097));+#1936=CARTESIAN_POINT('Ctrl Pts',(-0.318623469562359,0.0800278236676916,+-0.55758575587927));+#1937=CARTESIAN_POINT('Ctrl Pts',(-0.318461366050339,0.0802748324236238,+-0.558382767208303));+#1938=CARTESIAN_POINT('Ctrl Pts',(-0.317558298056176,0.0816509019422895,+-0.562822864854938));+#1939=CARTESIAN_POINT('Ctrl Pts',(-0.316690987084109,0.0826540312775419,+-0.567359782952332));+#1940=CARTESIAN_POINT('Ctrl Pts',(-0.315863361522256,0.0832839518709352,+-0.572025584907004));+#1941=CARTESIAN_POINT('Ctrl Pts',(-0.315631534673193,0.0834603994253092,+-0.573332526373484));+#1942=CARTESIAN_POINT('Ctrl Pts',(-0.315402807565937,0.0836072561332406,+-0.574649634644197));+#1943=CARTESIAN_POINT('Ctrl Pts',(-0.315177252320794,0.0837237775861357,+-0.575977669345297));+#1944=CARTESIAN_POINT('Origin',(-0.401430300293736,3.27064764160667E-13,+-0.603897806997888));+#1945=CARTESIAN_POINT('',(-0.4074829924761,0.1091847387634,-0.613036359306));+#1946=CARTESIAN_POINT('Ctrl Pts',(-0.315177252320765,-0.0837237775862577,+-0.575977669345281));+#1947=CARTESIAN_POINT('Ctrl Pts',(-0.315982932360323,-0.0833075646592761,+-0.571233948799839));+#1948=CARTESIAN_POINT('Ctrl Pts',(-0.316829077480521,-0.0825083940600536,+-0.566629764112063));+#1949=CARTESIAN_POINT('Ctrl Pts',(-0.317712220903149,-0.0813412205777783,+-0.562131121131347));+#1950=CARTESIAN_POINT('Ctrl Pts',(-0.317959367998807,-0.0810145878600535,+-0.560872178904524));+#1951=CARTESIAN_POINT('Ctrl Pts',(-0.31820940449216,-0.0806588209016876,+-0.55962153372759));+#1952=CARTESIAN_POINT('Ctrl Pts',(-0.318462217244658,-0.0802735359629575,+-0.558378584134861));+#1953=CARTESIAN_POINT('Ctrl Pts',(-0.31945754963054,-0.0787566560843827,+-0.553485049408312));+#1954=CARTESIAN_POINT('Ctrl Pts',(-0.320498977465188,-0.0767785891004961,+-0.548712878370134));+#1955=CARTESIAN_POINT('Ctrl Pts',(-0.321544013587449,-0.0743809761372636,+-0.544182150641016));+#1956=CARTESIAN_POINT('Ctrl Pts',(-0.32171291605479,-0.0739934653741517,+-0.543449878257321));+#1957=CARTESIAN_POINT('Ctrl Pts',(-0.321881921888042,-0.0735950998205491,+-0.542723877701984));+#1958=CARTESIAN_POINT('Ctrl Pts',(-0.322050864047548,-0.0731861735036912,+-0.542004608270453));+#1959=CARTESIAN_POINT('Ctrl Pts',(-0.323265087913333,-0.0702471315574471,+-0.536835063189169));+#1960=CARTESIAN_POINT('Ctrl Pts',(-0.324474825937907,-0.0667598105990379,+-0.532012175889732));+#1961=CARTESIAN_POINT('Ctrl Pts',(-0.325625596190383,-0.06280385623367,+-0.527624819713992));+#1962=CARTESIAN_POINT('Ctrl Pts',(-0.32677636644286,-0.0588479018682992,+-0.523237463538248));+#1963=CARTESIAN_POINT('Ctrl Pts',(-0.327868075100742,-0.0544234085407739,+-0.519286302475132));+#1964=CARTESIAN_POINT('Ctrl Pts',(-0.328847281066494,-0.0496315555383764,+-0.515847416376405));+#1965=CARTESIAN_POINT('Ctrl Pts',(-0.329826487032245,-0.0448397025359821,+-0.512408530277681));+#1966=CARTESIAN_POINT('Ctrl Pts',(-0.33069322895068,-0.0396801295741799,+-0.5094824707284));+#1967=CARTESIAN_POINT('Ctrl Pts',(-0.331402666665915,-0.0342802504191809,+-0.507126457866762));+#1968=CARTESIAN_POINT('Ctrl Pts',(-0.33211210438115,-0.0288803712641782,+-0.504770445005122));+#1969=CARTESIAN_POINT('Ctrl Pts',(-0.332664292384823,-0.0232394241567108,+-0.502984677337351));+#1970=CARTESIAN_POINT('Ctrl Pts',(-0.333035675132521,-0.0175059361046273,+-0.501795120901761));+#1971=CARTESIAN_POINT('Ctrl Pts',(-0.333341428972251,-0.0127856414817898,+-0.500815776949969));+#1972=CARTESIAN_POINT('Ctrl Pts',(-0.333524079573346,-0.00800425093680821,+-0.500234268944126));+#1973=CARTESIAN_POINT('Ctrl Pts',(-0.333579637568279,-0.00314219531358425,+-0.500057197209649));+#1974=CARTESIAN_POINT('Ctrl Pts',(-0.333591562882249,-0.00209857342053004,+-0.500019189430006));+#1975=CARTESIAN_POINT('Ctrl Pts',(-0.3335975848396,-0.00105123145341152,+-0.500000000000803));+#1976=CARTESIAN_POINT('Ctrl Pts',(-0.3335975848396,-1.07856518136231E-17,+-0.500000000000804));+#1977=CARTESIAN_POINT('',(-0.4074829924761,-0.1091847387634,-0.613036359306));+#1978=CARTESIAN_POINT('Ctrl Pts',(-0.411248496524204,0.10771075566218,-0.590000000000004));+#1979=CARTESIAN_POINT('Ctrl Pts',(-0.412335365106895,0.106754178723554,+-0.584287343180426));+#1980=CARTESIAN_POINT('Ctrl Pts',(-0.413450266924472,0.105367062274251,+-0.57876861799284));+#1981=CARTESIAN_POINT('Ctrl Pts',(-0.414588514085221,0.103566593934644,+-0.573407235034872));+#1982=CARTESIAN_POINT('Ctrl Pts',(-0.414919555429589,0.103042955940903,+-0.571847960842692));+#1983=CARTESIAN_POINT('Ctrl Pts',(-0.415252558744554,0.102484019571602,+-0.570302044395784));+#1984=CARTESIAN_POINT('Ctrl Pts',(-0.415587357854052,0.101889411542141,+-0.568768782073528));+#1985=CARTESIAN_POINT('Ctrl Pts',(-0.416868551661604,0.0996139929294409,+-0.562901364458308));+#1986=CARTESIAN_POINT('Ctrl Pts',(-0.418179100460899,0.0968126169863802,+-0.557221600045358));+#1987=CARTESIAN_POINT('Ctrl Pts',(-0.419472455342518,0.0935444334396831,+-0.55185371937497));+#1988=CARTESIAN_POINT('Ctrl Pts',(-0.419679172147034,0.0930220799318057,+-0.550995771448245));+#1989=CARTESIAN_POINT('Ctrl Pts',(-0.419885461010002,0.0924879001153955,+-0.550145747168357));+#1990=CARTESIAN_POINT('Ctrl Pts',(-0.420091146313179,0.0919422448301508,+-0.549304097975733));+#1991=CARTESIAN_POINT('Ctrl Pts',(-0.42158373275419,0.0879826149118071,+-0.543196543602156));+#1992=CARTESIAN_POINT('Ctrl Pts',(-0.423042803560813,0.0834187976153937,+-0.537528124716524));+#1993=CARTESIAN_POINT('Ctrl Pts',(-0.424419963758459,0.0783103373174552,+-0.532361072541459));+#1994=CARTESIAN_POINT('Ctrl Pts',(-0.425647567685791,0.0737566434660921,+-0.52775514974927));+#1995=CARTESIAN_POINT('Ctrl Pts',(-0.426810703038692,0.0687666605271557,+-0.523551336662487));+#1996=CARTESIAN_POINT('Ctrl Pts',(-0.42786246633763,0.063455892915773,-0.519838841449086));+#1997=CARTESIAN_POINT('Ctrl Pts',(-0.427990600333247,0.0628088938480838,+-0.519386556371366));+#1998=CARTESIAN_POINT('Ctrl Pts',(-0.428117093408138,0.0621571752986617,+-0.518941516825114));+#1999=CARTESIAN_POINT('Ctrl Pts',(-0.428241871215455,0.0615009786666518,+-0.518503843689486));+#2000=CARTESIAN_POINT('Ctrl Pts',(-0.429390863694495,0.0554585179826378,+-0.514473614661368));+#2001=CARTESIAN_POINT('Ctrl Pts',(-0.430392859164028,0.0490356625372045,+-0.511067306868829));+#2002=CARTESIAN_POINT('Ctrl Pts',(-0.431211799817661,0.0423348025936602,+-0.508318555235263));+#2003=CARTESIAN_POINT('Ctrl Pts',(-0.432030740471295,0.0356339426501112,+-0.505569803601695));+#2004=CARTESIAN_POINT('Ctrl Pts',(-0.432666700118526,0.0286530521634675,+-0.503483081871434));+#2005=CARTESIAN_POINT('Ctrl Pts',(-0.433093524597879,0.0215733298020928,+-0.502094248778484));+#2006=CARTESIAN_POINT('Ctrl Pts',(-0.433437698864707,0.0158645235575013,+-0.500974349066458));+#2007=CARTESIAN_POINT('Ctrl Pts',(-0.433645286200684,0.0100929345251606,+-0.500301088788609));+#2008=CARTESIAN_POINT('Ctrl Pts',(-0.433713401178734,0.00423523222649463,+-0.500079922611624));+#2009=CARTESIAN_POINT('Ctrl Pts',(-0.433729758349954,0.00282856012299885,+-0.500026811634736));+#2010=CARTESIAN_POINT('Ctrl Pts',(-0.4337380134347,0.00141691441391671,+-0.500000000000854));+#2011=CARTESIAN_POINT('Ctrl Pts',(-0.4337380134347,1.02443375240448E-17,+-0.50000000000086));+#2012=CARTESIAN_POINT('Origin',(-0.5797981035151,0.15535643361,-1.65));+#2013=CARTESIAN_POINT('Ctrl Pts',(0.421594773337783,3.49079407643331E-9,+-0.612965998035337));+#2014=CARTESIAN_POINT('Ctrl Pts',(0.42159477358289,0.421594778195061,-0.612965985033865));+#2015=CARTESIAN_POINT('Ctrl Pts',(-1.1213764284346E-9,0.421594778195061,+-0.612965980994025));+#2016=CARTESIAN_POINT('Ctrl Pts',(-0.421594775825643,0.42159477819506,-0.612965976954184));+#2017=CARTESIAN_POINT('Ctrl Pts',(-0.421594775580536,3.4907939215419E-9,+-0.612965989955655));+#2018=CARTESIAN_POINT('Ctrl Pts',(0.435000000034728,9.83699919706326E-10,+-0.531921933613032));+#2019=CARTESIAN_POINT('Ctrl Pts',(0.435000000287629,0.435000001616118,-0.53192192019816));+#2020=CARTESIAN_POINT('Ctrl Pts',(-3.44789210291156E-10,0.435000001616118,+-0.531921916029866));+#2021=CARTESIAN_POINT('Ctrl Pts',(-0.435000000977207,0.435000001616118,+-0.531921911861573));+#2022=CARTESIAN_POINT('Ctrl Pts',(-0.435000000724307,9.83699759889923E-10,+-0.531921925276445));+#2023=CARTESIAN_POINT('Ctrl Pts',(0.435000001180448,-1.54955962635399E-9,+-0.4497766934534));+#2024=CARTESIAN_POINT('Ctrl Pts',(0.435000001433349,0.434999999441439,-0.449776680038528));+#2025=CARTESIAN_POINT('Ctrl Pts',(4.4234978091698E-10,0.434999999441439,+-0.449776675870234));+#2026=CARTESIAN_POINT('Ctrl Pts',(-0.435000000548649,0.434999999441439,+-0.449776671701941));+#2027=CARTESIAN_POINT('Ctrl Pts',(-0.435000000295749,-1.54955978617039E-9,+-0.449776685116813));+#2028=CARTESIAN_POINT('Origin',(-0.2656296022295,-0.07117523740319,-1.65));+#2029=CARTESIAN_POINT('Ctrl Pts',(-0.326107911898371,-1.07988155587198E-9,+-0.575977675627819));+#2030=CARTESIAN_POINT('Ctrl Pts',(-0.32610791209425,-0.326107913554983,+-0.575977671012111));+#2031=CARTESIAN_POINT('Ctrl Pts',(3.80851438377975E-10,-0.326107913554983,+-0.575977669578031));+#2032=CARTESIAN_POINT('Ctrl Pts',(0.326107912855953,-0.326107913554983,+-0.575977668143951));+#2033=CARTESIAN_POINT('Ctrl Pts',(0.326107912660074,-1.07988137111427E-9,+-0.57597767275966));+#2034=CARTESIAN_POINT('Ctrl Pts',(-0.335000000013656,-1.85854481202705E-10,+-0.5131904516392));+#2035=CARTESIAN_POINT('Ctrl Pts',(-0.335000000214875,-0.33500000050547,+-0.513190446897633));+#2036=CARTESIAN_POINT('Ctrl Pts',(1.04740742565669E-10,-0.33500000050547,+-0.51319044542445));+#2037=CARTESIAN_POINT('Ctrl Pts',(0.335000000424357,-0.33500000050547,-0.513190443951267));+#2038=CARTESIAN_POINT('Ctrl Pts',(0.335000000223137,-1.8585429140715E-10,+-0.513190448692834));+#2039=CARTESIAN_POINT('Ctrl Pts',(-0.3350000004647,7.11699437773466E-10,+-0.4497766934534));+#2040=CARTESIAN_POINT('Ctrl Pts',(-0.335000000665919,-0.334999999780095,+-0.449776688711833));+#2041=CARTESIAN_POINT('Ctrl Pts',(-1.74125175346649E-10,-0.334999999780095,+-0.44977668723865));+#2042=CARTESIAN_POINT('Ctrl Pts',(0.335000000317669,-0.334999999780095,+-0.449776685765467));+#2043=CARTESIAN_POINT('Ctrl Pts',(0.33500000011645,7.1169962756902E-10,+-0.449776690507034));+#2044=CARTESIAN_POINT('Origin',(0.,0.,-0.4497766934534));+#2045=CARTESIAN_POINT('Ctrl Pts',(0.0837237775861505,-0.315177252320791,+-0.575977669345295));+#2046=CARTESIAN_POINT('Ctrl Pts',(0.0833111158288333,-0.315976058218939,+-0.571274422690266));+#2047=CARTESIAN_POINT('Ctrl Pts',(0.0825222653094782,-0.316814349478257,+-0.566710297403623));+#2048=CARTESIAN_POINT('Ctrl Pts',(0.081371829388118,-0.317689089762647,+-0.562249395052968));+#2049=CARTESIAN_POINT('Ctrl Pts',(0.0809974744755795,-0.31797373258071,+-0.560797805548228));+#2050=CARTESIAN_POINT('Ctrl Pts',(0.0805844192922588,-0.318262225002428,+-0.559357184135653));+#2051=CARTESIAN_POINT('Ctrl Pts',(0.0801320089383487,-0.318554400391273,+-0.5579265335182));+#2052=CARTESIAN_POINT('Ctrl Pts',(0.0786476942517575,-0.319512999629718,+-0.553232706985467));+#2053=CARTESIAN_POINT('Ctrl Pts',(0.0767358952255177,-0.32051462386449,+-0.548647889163378));+#2054=CARTESIAN_POINT('Ctrl Pts',(0.074437164903471,-0.321519327508318,+-0.544288562317644));+#2055=CARTESIAN_POINT('Ctrl Pts',(0.073882109826671,-0.321761924846875,+-0.543235952329448));+#2056=CARTESIAN_POINT('Ctrl Pts',(0.07330472111427,-0.322004721617662,-0.542196412445231));+#2057=CARTESIAN_POINT('Ctrl Pts',(0.072705987626208,-0.322247201405081,+-0.541171405006137));+#2058=CARTESIAN_POINT('Ctrl Pts',(0.0696276320585324,-0.323493898004706,+-0.535901385172677));+#2059=CARTESIAN_POINT('Ctrl Pts',(0.0659867117412812,-0.3247298119796,-0.531011949183155));+#2060=CARTESIAN_POINT('Ctrl Pts',(0.0618170704505808,-0.325908710579265,+-0.526549788856714));+#2061=CARTESIAN_POINT('Ctrl Pts',(0.0583291747335857,-0.326894856650322,+-0.522817201613333));+#2062=CARTESIAN_POINT('Ctrl Pts',(0.0544671658224409,-0.327841935320495,+-0.519388553988434));+#2063=CARTESIAN_POINT('Ctrl Pts',(0.0503267876586513,-0.328703861003485,+-0.51635182076558));+#2064=CARTESIAN_POINT('Ctrl Pts',(0.0495175075896181,-0.328872333362443,+-0.515758259604013));+#2065=CARTESIAN_POINT('Ctrl Pts',(0.048697707444218,-0.329037580393907,+-0.515179563557084));+#2066=CARTESIAN_POINT('Ctrl Pts',(0.0478682583864593,-0.329199301338863,+-0.514616221947355));+#2067=CARTESIAN_POINT('Ctrl Pts',(0.0430885501137471,-0.330131219859051,+-0.511369960435331));+#2068=CARTESIAN_POINT('Ctrl Pts',(0.0379837795073046,-0.330944869732061,+-0.508635202763097));+#2069=CARTESIAN_POINT('Ctrl Pts',(0.0327038596836068,-0.331604453805711,+-0.506457571848595));+#2070=CARTESIAN_POINT('Ctrl Pts',(0.0323798582626379,-0.33164492907508,+-0.506323941881815));+#2071=CARTESIAN_POINT('Ctrl Pts',(0.0320552022114266,-0.331684828084336,+-0.506192394799861));+#2072=CARTESIAN_POINT('Ctrl Pts',(0.0317299278341516,-0.331724144107688,+-0.50606293517134));+#2073=CARTESIAN_POINT('Ctrl Pts',(0.0264357949832631,-0.332364047725485,+-0.503955863357209));+#2074=CARTESIAN_POINT('Ctrl Pts',(0.0209804384401526,-0.332847979990919,+-0.502398231947721));+#2075=CARTESIAN_POINT('Ctrl Pts',(0.0153931995384828,-0.333163848411464,+-0.501384326428245));+#2076=CARTESIAN_POINT('Ctrl Pts',(0.0150430239408443,-0.333183645204636,+-0.50132078073466));+#2077=CARTESIAN_POINT('Ctrl Pts',(0.0146923254682716,-0.333202777757167,+-0.501259386577382));+#2078=CARTESIAN_POINT('Ctrl Pts',(0.0143411099383942,-0.333221241426901,+-0.501200148983153));+#2079=CARTESIAN_POINT('Ctrl Pts',(0.0110667058138217,-0.333393379362701,+-0.500647873225472));+#2080=CARTESIAN_POINT('Ctrl Pts',(0.0076956806714656,-0.333509258668981,+-0.500278623506273));+#2081=CARTESIAN_POINT('Ctrl Pts',(0.00435443160267475,-0.333562682635347,+-0.500109879633224));+#2082=CARTESIAN_POINT('Ctrl Pts',(0.00289360381057889,-0.333586040132266,+-0.500036103110007));+#2083=CARTESIAN_POINT('Ctrl Pts',(0.00143856591257334,-0.333597628343911,+-0.500000000000339));+#2084=CARTESIAN_POINT('Ctrl Pts',(2.93270070027646E-17,-0.333597713908903,+-0.50000000000034));+#2085=CARTESIAN_POINT('Ctrl Pts',(-0.00125511588470852,-0.333597788562403,+-0.500000000000341));+#2086=CARTESIAN_POINT('Ctrl Pts',(-0.00252386067423867,-0.333589116858328,+-0.500027445011607));+#2087=CARTESIAN_POINT('Ctrl Pts',(-0.00379977072823687,-0.333571264107733,+-0.500083655844876));+#2088=CARTESIAN_POINT('Ctrl Pts',(-0.00827914637985656,-0.333508587921369,+-0.500280996901259));+#2089=CARTESIAN_POINT('Ctrl Pts',(-0.0127493477274121,-0.333334440970801,+-0.500831842599127));+#2090=CARTESIAN_POINT('Ctrl Pts',(-0.0170872634537396,-0.333061637479901,+-0.501709347446107));+#2091=CARTESIAN_POINT('Ctrl Pts',(-0.0186403932013131,-0.332963964024792,+-0.50202352572145));+#2092=CARTESIAN_POINT('Ctrl Pts',(-0.0201766758888196,-0.332853763737257,+-0.502379114998292));+#2093=CARTESIAN_POINT('Ctrl Pts',(-0.0216909860379876,-0.332732009414287,+-0.502773396916482));+#2094=CARTESIAN_POINT('Ctrl Pts',(-0.0267562157220168,-0.332324752280599,+-0.504092234062882));+#2095=CARTESIAN_POINT('Ctrl Pts',(-0.0315755298036749,-0.331785759560259,+-0.505846607674932));+#2096=CARTESIAN_POINT('Ctrl Pts',(-0.0360562914756588,-0.331162420758599,+-0.507925849167792));+#2097=CARTESIAN_POINT('Ctrl Pts',(-0.0366565646304176,-0.331078914074613,+-0.50820439842834));+#2098=CARTESIAN_POINT('Ctrl Pts',(-0.0372508270530885,-0.330993903647512,+-0.508488739393776));+#2099=CARTESIAN_POINT('Ctrl Pts',(-0.037838917703469,-0.330907511812341,+-0.508778573543225));+#2100=CARTESIAN_POINT('Ctrl Pts',(-0.0428168332610387,-0.330176244855344,+-0.511231885665634));+#2101=CARTESIAN_POINT('Ctrl Pts',(-0.0473472492205696,-0.329345118465104,+-0.5140786908808));+#2102=CARTESIAN_POINT('Ctrl Pts',(-0.0513936421593418,-0.328478679793113,+-0.517147531179651));+#2103=CARTESIAN_POINT('Ctrl Pts',(-0.0554400350981078,-0.327612241121124,+-0.520216371478497));+#2104=CARTESIAN_POINT('Ctrl Pts',(-0.0590031424215731,-0.326710333133054,+-0.523507601757999));+#2105=CARTESIAN_POINT('Ctrl Pts',(-0.0620885878272953,-0.325831595671887,+-0.526841778556864));+#2106=CARTESIAN_POINT('Ctrl Pts',(-0.0642649071387008,-0.325211778091306,+-0.529193540588223));+#2107=CARTESIAN_POINT('Ctrl Pts',(-0.0662076623982693,-0.324601923187377,+-0.531562698654379));+#2108=CARTESIAN_POINT('Ctrl Pts',(-0.0680015401083481,-0.323990644618283,+-0.534006096246686));+#2109=CARTESIAN_POINT('Ctrl Pts',(-0.0687509067236727,-0.323735291786945,+-0.535026790451956));+#2110=CARTESIAN_POINT('Ctrl Pts',(-0.0694738521432972,-0.32347957482299,+-0.536060886706243));+#2111=CARTESIAN_POINT('Ctrl Pts',(-0.0701745418453286,-0.323222227201006,+-0.537114215206616));+#2112=CARTESIAN_POINT('Ctrl Pts',(-0.072033378926197,-0.322539518006008,+-0.539908556366695));+#2113=CARTESIAN_POINT('Ctrl Pts',(-0.0737365909720407,-0.321847414119789,+-0.542833725944208));+#2114=CARTESIAN_POINT('Ctrl Pts',(-0.075300166211154,-0.32113446084313,+-0.545971700982215));+#2115=CARTESIAN_POINT('Ctrl Pts',(-0.0757368992613711,-0.320935320916001,+-0.546848190565556));+#2116=CARTESIAN_POINT('Ctrl Pts',(-0.0761624527948228,-0.320734525318884,+-0.547741395009664));+#2117=CARTESIAN_POINT('Ctrl Pts',(-0.0765764688546511,-0.320531766745636,+-0.548653323627318));+#2118=CARTESIAN_POINT('Ctrl Pts',(-0.0778389153986709,-0.319913501212234,+-0.551434039747917));+#2119=CARTESIAN_POINT('Ctrl Pts',(-0.0789913370857365,-0.319279815347515,+-0.554392348560699));+#2120=CARTESIAN_POINT('Ctrl Pts',(-0.0799696098400156,-0.318658285490801,+-0.557418687928551));+#2121=CARTESIAN_POINT('Ctrl Pts',(-0.0804607539645729,-0.318346244972421,+-0.558938068687073));+#2122=CARTESIAN_POINT('Ctrl Pts',(-0.0809087283260045,-0.318037363319987,+-0.560474230464428));+#2123=CARTESIAN_POINT('Ctrl Pts',(-0.0813100968126008,-0.317735567303609,+-0.562011619807543));+#2124=CARTESIAN_POINT('Ctrl Pts',(-0.0822676972353884,-0.317015530722435,+-0.565679582625054));+#2125=CARTESIAN_POINT('Ctrl Pts',(-0.0829651131575208,-0.316330591452659,+-0.56935459406944));+#2126=CARTESIAN_POINT('Ctrl Pts',(-0.083422590702711,-0.315673112071725,+-0.573104483991684));+#2127=CARTESIAN_POINT('Ctrl Pts',(-0.0835387866656639,-0.315506117099856,+-0.574056928707416));+#2128=CARTESIAN_POINT('Ctrl Pts',(-0.0836392483192254,-0.315340879009581,+-0.57501426048914));+#2129=CARTESIAN_POINT('Ctrl Pts',(-0.0837237775860824,-0.315177252320807,+-0.575977669345304));+#2130=CARTESIAN_POINT('Origin',(-1.39731975989932E-13,-0.401200492639971,+-0.603838328400014));+#2131=CARTESIAN_POINT('',(-0.1091167579479,-0.4072292846186,-0.612965980387));+#2132=CARTESIAN_POINT('',(0.1091167579479,-0.4072292846186,-0.612965980387));+#2133=CARTESIAN_POINT('Origin',(-0.15535643361,-0.5797981035151,-1.65));+#2134=CARTESIAN_POINT('Origin',(0.07117523740319,-0.2656296022295,-1.65));+#2135=CARTESIAN_POINT('Origin',(0.,0.,0.0825));+#2136=CARTESIAN_POINT('',(-0.335,4.10256677714363E-17,0.0825));+#2137=CARTESIAN_POINT('Origin',(0.5797981035151,-0.15535643361,-1.65));+#2138=CARTESIAN_POINT('Origin',(-0.499999999999994,-0.331662479035541,-2.6));+#2139=CARTESIAN_POINT('',(-0.499999999999994,-0.331662479035541,-2.435));+#2140=CARTESIAN_POINT('',(-0.499999999999994,0.331662479035541,-2.435));+#2141=CARTESIAN_POINT('Ctrl Pts',(-0.499999999999994,-0.331662479035542,+-2.435));+#2142=CARTESIAN_POINT('Ctrl Pts',(-0.499999999999994,-3.46944695195361E-17,+-2.61833333333334));+#2143=CARTESIAN_POINT('Ctrl Pts',(-0.499999999999994,0.331662479035542,+-2.435));+#2144=CARTESIAN_POINT('',(-0.499999999999995,-0.331662479035541,-1.11022302462516E-15));+#2145=CARTESIAN_POINT('',(-0.499999999999995,-0.331662479035542,-1.3));+#2146=CARTESIAN_POINT('',(-0.499999999999996,0.331662479035541,-1.11022302462516E-15));+#2147=CARTESIAN_POINT('',(-0.499999999999995,-0.165831239517771,-8.32667268468867E-16));+#2148=CARTESIAN_POINT('',(-0.499999999999995,0.331662479035542,-1.3));+#2149=CARTESIAN_POINT('Origin',(-0.331662479035539,0.499999999999997,-2.6));+#2150=CARTESIAN_POINT('',(-0.331662479035539,0.499999999999997,-2.435));+#2151=CARTESIAN_POINT('',(0.331662479035544,0.499999999999997,-2.435));+#2152=CARTESIAN_POINT('Ctrl Pts',(-0.33166247903554,0.499999999999997,-2.435));+#2153=CARTESIAN_POINT('Ctrl Pts',(2.96517198614249E-15,0.499999999999997,+-2.61833333333334));+#2154=CARTESIAN_POINT('Ctrl Pts',(0.331662479035545,0.499999999999997,-2.435));+#2155=CARTESIAN_POINT('',(-0.33166247903554,0.499999999999997,-5.55111512312578E-16));+#2156=CARTESIAN_POINT('',(-0.33166247903554,0.499999999999997,-1.3));+#2157=CARTESIAN_POINT('',(0.331662479035543,0.499999999999997,-5.55111512312578E-16));+#2158=CARTESIAN_POINT('',(-0.16583123951777,0.499999999999997,-5.55111512312578E-16));+#2159=CARTESIAN_POINT('',(0.331662479035543,0.499999999999997,-1.3));+#2160=CARTESIAN_POINT('Origin',(0.331662479035544,-0.499999999999997,-2.6));+#2161=CARTESIAN_POINT('',(0.331662479035544,-0.499999999999997,-2.435));+#2162=CARTESIAN_POINT('',(-0.331662479035538,-0.499999999999997,-2.435));+#2163=CARTESIAN_POINT('Ctrl Pts',(0.331662479035545,-0.499999999999997,+-2.435));+#2164=CARTESIAN_POINT('Ctrl Pts',(3.11599655502603E-15,-0.499999999999997,+-2.61833333333334));+#2165=CARTESIAN_POINT('Ctrl Pts',(-0.33166247903554,-0.499999999999997,+-2.435));+#2166=CARTESIAN_POINT('',(0.331662479035543,-0.499999999999997,-5.55111512312578E-16));+#2167=CARTESIAN_POINT('',(0.331662479035544,-0.499999999999997,-1.3));+#2168=CARTESIAN_POINT('',(-0.33166247903554,-0.499999999999997,-5.55111512312578E-16));+#2169=CARTESIAN_POINT('',(0.165831239517772,-0.499999999999997,-5.55111512312578E-16));+#2170=CARTESIAN_POINT('',(-0.331662479035539,-0.499999999999997,-1.3));+#2171=CARTESIAN_POINT('Origin',(0.499999999999999,0.331662479035541,-2.6));+#2172=CARTESIAN_POINT('',(0.499999999999999,0.331662479035541,-2.435));+#2173=CARTESIAN_POINT('',(0.499999999999999,5.42714901056402E-17,-2.535));+#2174=CARTESIAN_POINT('Ctrl Pts',(0.499999999999999,0.331662479035541,-2.435));+#2175=CARTESIAN_POINT('Ctrl Pts',(0.499999999999999,0.150755672288883,-2.535));+#2176=CARTESIAN_POINT('Ctrl Pts',(0.5,4.73106402539131E-17,-2.535));+#2177=CARTESIAN_POINT('',(0.499999999999998,0.331662479035541,0.));+#2178=CARTESIAN_POINT('',(0.499999999999999,0.331662479035542,-1.3));+#2179=CARTESIAN_POINT('',(0.499999999999998,-0.331662479035541,0.));+#2180=CARTESIAN_POINT('',(0.499999999999998,0.165831239517771,-2.77555756156289E-16));+#2181=CARTESIAN_POINT('',(0.499999999999999,-0.331662479035541,-2.435));+#2182=CARTESIAN_POINT('',(0.499999999999999,-0.331662479035541,-1.3));+#2183=CARTESIAN_POINT('Ctrl Pts',(0.5,4.73106402539131E-17,-2.535));+#2184=CARTESIAN_POINT('Ctrl Pts',(0.499999999999999,-0.150755672288883,+-2.535));+#2185=CARTESIAN_POINT('Ctrl Pts',(0.5,-0.331662479035541,-2.435));+#2186=CARTESIAN_POINT('Origin',(2.02718884003944E-15,0.,-1.3));+#2187=CARTESIAN_POINT('Origin',(2.72791930683599E-15,0.,-2.435));+#2188=CARTESIAN_POINT('Origin',(1.22459006749714E-15,0.,-5.55111512312578E-16));+#2189=CARTESIAN_POINT('Origin',(2.02718884003944E-15,0.,-1.3));+#2190=CARTESIAN_POINT('Origin',(2.72791930683599E-15,0.,-2.435));+#2191=CARTESIAN_POINT('Origin',(1.22459006749714E-15,0.,-5.55111512312578E-16));+#2192=CARTESIAN_POINT('Origin',(2.02718884003944E-15,0.,-1.3));+#2193=CARTESIAN_POINT('Origin',(1.22459006749714E-15,0.,-5.55111512312578E-16));+#2194=CARTESIAN_POINT('Origin',(2.72791930683599E-15,0.,-2.435));+#2195=CARTESIAN_POINT('Origin',(2.77885345970887E-15,0.,-2.5175));+#2196=CARTESIAN_POINT('Origin',(2.72791930683599E-15,0.,-2.435));+#2197=CARTESIAN_POINT('',(0.435,5.32721357629095E-17,-2.6));+#2198=CARTESIAN_POINT('',(0.5175,6.33754718558752E-17,-2.5175));+#2199=CARTESIAN_POINT('Origin',(2.82978761258174E-15,0.,-2.6));+#2200=CARTESIAN_POINT('Origin',(0.6,0.,-2.6));+#2201=CARTESIAN_POINT('',(-0.254999999999994,-3.12284933782571E-17,-2.6));+#2202=CARTESIAN_POINT('Origin',(2.82978761258174E-15,0.,-2.6));+#2203=CARTESIAN_POINT('Origin',(2.02718884003944E-15,0.,-1.3));+#2204=CARTESIAN_POINT('Origin',(1.22459006749714E-15,0.,-5.55111512312578E-16));+#2205=CARTESIAN_POINT('Origin',(-1.63567151866233E-16,0.,0.0993128153615053));+#2206=CARTESIAN_POINT('',(-0.435,5.32721357629099E-17,0.229999999999999));+#2207=CARTESIAN_POINT('Origin',(-2.24224988560958E-16,0.,0.23));+#2208=CARTESIAN_POINT('',(-1.02909315171507E-16,0.,-0.0313743692769891));+#2209=CARTESIAN_POINT('',(-0.2175,2.66360678814549E-17,0.0993128153615052));+#2210=CARTESIAN_POINT('Origin',(-1.4252008369715E-15,0.,2.8175));+#2211=CARTESIAN_POINT('',(-0.600000000000002,7.34788079488412E-17,2.9));+#2212=CARTESIAN_POINT('Origin',(-1.46349282054401E-15,0.,2.9));+#2213=CARTESIAN_POINT('',(-0.435000000000001,5.32721357629099E-17,2.735));+#2214=CARTESIAN_POINT('',(-0.517500000000002,6.33754718558755E-17,2.8175));+#2215=CARTESIAN_POINT('Origin',(-1.38690885339899E-15,0.,2.735));+#2216=CARTESIAN_POINT('Origin',(-8.43858904552486E-16,0.,1.565));+#2217=CARTESIAN_POINT('',(-0.435000000000001,5.32721357629099E-17,1.565));+#2218=CARTESIAN_POINT('Origin',(-5.55111512312578E-16,0.,2.9));+#2219=CARTESIAN_POINT('',(-0.800000000000001,-9.79717439317884E-17,2.9));+#2220=CARTESIAN_POINT('Origin',(-5.65822578943377E-16,0.,2.9));+#2221=CARTESIAN_POINT('Origin',(4.04991084555001E-15,0.,-4.505));+#2222=CARTESIAN_POINT('',(-0.254999999999993,-3.12284933782572E-17,-4.50499999490833));+#2223=CARTESIAN_POINT('Origin',(4.0059035030483E-15,0.,-4.50499999490833));+#2224=CARTESIAN_POINT('',(4.03429659886088E-15,-1.91219372289385E-33,-4.76));+#2225=CARTESIAN_POINT('Origin',(4.04991084555001E-15,0.,-4.505));+#2226=CARTESIAN_POINT('Origin',(3.41784555781502E-15,0.,-3.55249999745417));+#2227=CARTESIAN_POINT('',(-0.254999999999993,-3.12284933782571E-17,-3.55249999745417));+#2228=CARTESIAN_POINT('Origin',(0.8,0.,3.05772170352758E-17));+#2229=CARTESIAN_POINT('',(-0.799999999999998,-9.79717439317881E-17,-1.16226472890446E-15));+#2230=CARTESIAN_POINT('Origin',(1.22459006749714E-15,0.,-5.55111512312578E-16));+#2231=CARTESIAN_POINT('Origin',(1.1628517003785E-15,0.,0.0999999999999995));+#2232=CARTESIAN_POINT('',(-0.799999999999998,-9.79717439317881E-17,0.199999999999999));+#2233=CARTESIAN_POINT('Origin',(1.10111333325986E-15,0.,0.2));+#2234=CARTESIAN_POINT('',(-0.799999999999998,-9.79717439317881E-17,0.099999999999999));+#2235=CARTESIAN_POINT('Origin',(0.6,0.,0.199999999999999));+#2236=CARTESIAN_POINT('',(-0.599999999999998,-7.34788079488411E-17,0.199999999999999));+#2237=CARTESIAN_POINT('Origin',(1.10111333325987E-15,0.,0.199999999999999));+#2238=CARTESIAN_POINT('Origin',(5.14598845632798E-16,0.,1.15));+#2239=CARTESIAN_POINT('',(-0.599999999999999,-7.34788079488411E-17,2.1));+#2240=CARTESIAN_POINT('Origin',(-7.19156419942695E-17,0.,2.1));+#2241=CARTESIAN_POINT('',(-0.599999999999998,-7.34788079488411E-17,1.15));+#2242=CARTESIAN_POINT('Origin',(0.8,0.,2.1));+#2243=CARTESIAN_POINT('',(-0.800000000000001,-9.79717439317884E-17,2.1));+#2244=CARTESIAN_POINT('Origin',(-7.19156419942684E-17,0.,2.1));+#2245=CARTESIAN_POINT('Origin',(-3.18869110468822E-16,0.,2.5));+#2246=CARTESIAN_POINT('',(-0.800000000000001,-9.79717439317884E-17,2.5));+#2247=CARTESIAN_POINT('',(-1.14227611120156E-15,0.,2.74));+#2248=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.01),#2254,+'DISTANCE_ACCURACY_VALUE',+'Maximum model space distance between geometric entities at asserted c+onnectivities');+#2249=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.01),#2254,+'DISTANCE_ACCURACY_VALUE',+'Maximum model space distance between geometric entities at asserted c+onnectivities');+#2250=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.01),#2254,+'DISTANCE_ACCURACY_VALUE',+'Maximum model space distance between geometric entities at asserted c+onnectivities');+#2251=(+GEOMETRIC_REPRESENTATION_CONTEXT(3)+GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2248))+GLOBAL_UNIT_ASSIGNED_CONTEXT((#2254,#2256,#2257))+REPRESENTATION_CONTEXT('','3D')+);+#2252=(+GEOMETRIC_REPRESENTATION_CONTEXT(3)+GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2249))+GLOBAL_UNIT_ASSIGNED_CONTEXT((#2254,#2256,#2257))+REPRESENTATION_CONTEXT('','3D')+);+#2253=(+GEOMETRIC_REPRESENTATION_CONTEXT(3)+GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2250))+GLOBAL_UNIT_ASSIGNED_CONTEXT((#2254,#2256,#2257))+REPRESENTATION_CONTEXT('','3D')+);+#2254=(+LENGTH_UNIT()+NAMED_UNIT(*)+SI_UNIT(.MILLI.,.METRE.)+);+#2255=(+LENGTH_UNIT()+NAMED_UNIT(*)+SI_UNIT($,.METRE.)+);+#2256=(+NAMED_UNIT(*)+PLANE_ANGLE_UNIT()+SI_UNIT($,.RADIAN.)+);+#2257=(+NAMED_UNIT(*)+SI_UNIT($,.STERADIAN.)+SOLID_ANGLE_UNIT()+);+#2258=SHAPE_DEFINITION_REPRESENTATION(#2260,#2263);+#2259=SHAPE_DEFINITION_REPRESENTATION(#2261,#2264);+#2260=PRODUCT_DEFINITION_SHAPE('',$,#2266);+#2261=PRODUCT_DEFINITION_SHAPE('',$,#2267);+#2262=PRODUCT_DEFINITION_SHAPE($,$,#14);+#2263=SHAPE_REPRESENTATION('',(#940,#1054),#2251);+#2264=SHAPE_REPRESENTATION('',(#941),#2252);+#2265=PRODUCT_DEFINITION_CONTEXT('part definition',#2273,'design');+#2266=PRODUCT_DEFINITION('MachinePin_Medium_Short',+'MachinePin_Medium_Short v28',#2268,#2265);+#2267=PRODUCT_DEFINITION('MpinContact_Medium','MpinContact_Medium v6',#2269,+#2265);+#2268=PRODUCT_DEFINITION_FORMATION('',$,#2275);+#2269=PRODUCT_DEFINITION_FORMATION('',$,#2276);+#2270=PRODUCT_RELATED_PRODUCT_CATEGORY('MachinePin_Medium_Short v28',+'MachinePin_Medium_Short v28',(#2275));+#2271=PRODUCT_RELATED_PRODUCT_CATEGORY('MpinContact_Medium v6',+'MpinContact_Medium v6',(#2276));+#2272=APPLICATION_PROTOCOL_DEFINITION('international standard',+'automotive_design',2009,#2273);+#2273=APPLICATION_CONTEXT(+'Core Data for Automotive Mechanical Design Process');+#2274=PRODUCT_CONTEXT('part definition',#2273,'mechanical');+#2275=PRODUCT('MachinePin_Medium_Short','MachinePin_Medium_Short v28',$,+(#2274));+#2276=PRODUCT('MpinContact_Medium','MpinContact_Medium v6',$,(#2274));+#2277=PRESENTATION_STYLE_ASSIGNMENT((#2279));+#2278=PRESENTATION_STYLE_ASSIGNMENT((#2280));+#2279=SURFACE_STYLE_USAGE(.BOTH.,#2281);+#2280=SURFACE_STYLE_USAGE(.BOTH.,#2282);+#2281=SURFACE_SIDE_STYLE('',(#2283));+#2282=SURFACE_SIDE_STYLE('',(#2284));+#2283=SURFACE_STYLE_FILL_AREA(#2285);+#2284=SURFACE_STYLE_FILL_AREA(#2286);+#2285=FILL_AREA_STYLE('Brass - Polished',(#2287));+#2286=FILL_AREA_STYLE('Copper - Polished',(#2288));+#2287=FILL_AREA_STYLE_COLOUR('Brass - Polished',#2289);+#2288=FILL_AREA_STYLE_COLOUR('Copper - Polished',#2290);+#2289=COLOUR_RGB('Brass - Polished',0.952941176470588,0.796078431372549,+0.486274509803922);+#2290=COLOUR_RGB('Copper - Polished',0.929411764705882,0.807843137254902,+0.764705882352941);+ENDSEC;+END-ISO-10303-21;
tour-pack-rp2040/AdomRP2040.3dshapes/MediumMachineContact v1.step added+2419
@@ -0,0 +1,2419 @@+ISO-10303-21;+HEADER;+/* Generated by software containing ST-Developer+ * from STEP Tools, Inc. (www.steptools.com) + */++FILE_DESCRIPTION(+/* description */ (''),+/* implementation_level */ '2;1');++FILE_NAME(+/* name */ 'MediumMachineContact v1.step',+/* time_stamp */ '2024-01-24T14:54:37-05:00',+/* author */ (''),+/* organization */ (''),+/* preprocessor_version */ 'ST-DEVELOPER v20',+/* originating_system */ 'Autodesk Translation Framework v12.14.0.127',++/* authorisation */ '');++FILE_SCHEMA (('AUTOMOTIVE_DESIGN { 1 0 10303 214 3 1 1 }'));+ENDSEC;++DATA;+#10=MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(#13),#1643);+#11=SHAPE_REPRESENTATION_RELATIONSHIP('SRR','None',#1650,#12);+#12=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#14),#1642);+#13=STYLED_ITEM('',(#1659),#14);+#14=MANIFOLD_SOLID_BREP('Body1',#585);+#15=ELLIPSE('',#613,0.123303040813508,0.117733653429061);+#16=ELLIPSE('',#617,0.123303040810181,0.117733653427394);+#17=ELLIPSE('',#621,0.123300822678697,0.117732417193227);+#18=ELLIPSE('',#624,0.12330082267097,0.117732417188975);+#19=ELLIPSE('',#626,0.0946949526236447,0.0904878540173368);+#20=ELLIPSE('',#627,0.0946949527732227,0.0904878541006801);+#21=ELLIPSE('',#640,0.0947014027658022,0.0904914268414502);+#22=ELLIPSE('',#641,0.0947013568795386,0.0904914015032144);+#23=ELLIPSE('',#648,0.0947014129570492,0.0904914324665984);+#24=ELLIPSE('',#651,0.0946959223869005,0.0904883921275694);+#25=ELLIPSE('',#654,0.0947012752311686,0.09049135641962);+#26=ELLIPSE('',#655,0.0946959221538525,0.0904883919975931);+#27=CONICAL_SURFACE('',#611,0.3500599520011,0.140686945962564);+#28=CONICAL_SURFACE('',#614,0.3500599520011,0.140686945962564);+#29=CONICAL_SURFACE('',#618,0.3500599520011,0.140686945962564);+#30=CONICAL_SURFACE('',#622,0.3500599520011,0.140686945962564);+#31=CONICAL_SURFACE('',#625,0.0854901781157201,0.25326156346064);+#32=CONICAL_SURFACE('',#639,0.107259882566625,0.253261563450098);+#33=CONICAL_SURFACE('',#644,0.2500550224821,0.140686945962564);+#34=CONICAL_SURFACE('',#646,0.2500550224821,0.140686945962564);+#35=CONICAL_SURFACE('',#649,0.2500550224821,0.140686945962564);+#36=CONICAL_SURFACE('',#652,0.2500550224821,0.140686945962564);+#37=CONICAL_SURFACE('',#659,0.107321319983375,0.253261563452846);+#38=CONICAL_SURFACE('',#663,0.107259881520342,0.253261563456436);+#39=B_SPLINE_CURVE_WITH_KNOTS('',3,(#875,#876,#877,#878,#879,#880,#881,+#882,#883,#884,#885,#886,#887,#888,#889,#890,#891,#892,#893,#894,#895,#896,+#897,#898,#899,#900,#901,#902,#903,#904,#905,#906,#907,#908,#909,#910,#911,+#912,#913,#914,#915,#916,#917,#918,#919,#920,#921,#922,#923,#924,#925,#926,+#927,#928,#929,#930,#931,#932,#933,#934,#935,#936,#937,#938,#939,#940,#941,+#942,#943,#944,#945,#946,#947),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,+3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4),(0.,0.0458743804394104,0.06666666666667,+0.111053365481494,0.1333333333333,0.187608648622586,0.2,0.2666666666667,+0.315138133753424,0.3333333333333,0.4,0.4666666666667,0.500062603112874,+0.5333333333333,0.6,0.6666666666667,0.71505202633289,0.7333333333333,0.8,+0.852985530890195,0.8666666666667,0.909980948728713,0.9333333333333,0.978752310399226,+1.),.UNSPECIFIED.);+#40=B_SPLINE_CURVE_WITH_KNOTS('',3,(#953,#954,#955,#956,#957,#958,#959,+#960,#961,#962,#963,#964,#965,#966,#967,#968,#969,#970,#971,#972,#973,#974,+#975,#976,#977,#978,#979,#980,#981,#982,#983,#984,#985,#986),+ .UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,3,4),(0.,0.110670393651878,+0.1428571428571,0.266027911824754,0.2857142857143,0.4285714285714,0.555914630849129,+0.5714285714286,0.7142857142857,0.8571428571429,0.972337169516299,1.),+ .UNSPECIFIED.);+#41=B_SPLINE_CURVE_WITH_KNOTS('',3,(#990,#991,#992,#993,#994,#995,#996,+#997,#998,#999,#1000,#1001,#1002,#1003,#1004,#1005,#1006,#1007,#1008,#1009,+#1010,#1011,#1012,#1013,#1014,#1015,#1016,#1017,#1018,#1019,#1020,#1021,+#1022,#1023),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,3,4),(0.,0.115194312373676,+0.1428571428571,0.2857142857143,0.4285714285714,0.555914630850625,0.5714285714286,+0.7142857142857,0.837456483254619,0.8571428571429,0.967813250795107,1.),+ .UNSPECIFIED.);+#42=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1065,#1066,#1067,#1068,#1069,#1070,+#1071,#1072,#1073,#1074,#1075,#1076,#1077,#1078,#1079,#1080,#1081,#1082,+#1083,#1084,#1085,#1086,#1087,#1088,#1089,#1090,#1091,#1092,#1093,#1094,+#1095,#1096,#1097,#1098),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,3,4),+(0.,0.115194312373344,0.1428571428571,0.2857142857143,0.4285714285714,0.55591463084967,+0.5714285714286,0.7142857142857,0.837456483253965,0.8571428571429,0.967813250794925,+1.),.UNSPECIFIED.);+#43=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1104,#1105,#1106,#1107,#1108,#1109,+#1110,#1111,#1112,#1113,#1114,#1115,#1116,#1117,#1118,#1119,#1120,#1121,+#1122,#1123,#1124,#1125,#1126,#1127,#1128,#1129,#1130,#1131,#1132,#1133,+#1134),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,4),(0.,0.11760285124474,+0.1428571428571,0.2857142857143,0.4285714285714,0.5714285714286,0.7142857142857,+0.837110761937757,0.8571428571429,0.968740340808658,1.),.UNSPECIFIED.);+#44=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1136,#1137,#1138,#1139,#1140,#1141,+#1142,#1143,#1144,#1145,#1146,#1147,#1148,#1149,#1150,#1151,#1152,#1153,+#1154,#1155,#1156,#1157,#1158,#1159,#1160,#1161,#1162,#1163,#1164,#1165,+#1166),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,4),(0.,0.111620281731672,+0.1428571428571,0.265837725142949,0.2857142857143,0.4285714285714,0.5714285714286,+0.7142857142857,0.8571428571429,0.974754997803736,1.),.UNSPECIFIED.);+#45=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1207,#1208,#1209,#1210,#1211,#1212,+#1213,#1214,#1215,#1216,#1217,#1218,#1219,#1220,#1221,#1222,#1223,#1224,+#1225,#1226,#1227,#1228,#1229,#1230,#1231,#1232,#1233,#1234,#1235,#1236,+#1237,#1238,#1239,#1240,#1241,#1242,#1243,#1244,#1245,#1246,#1247,#1248,+#1249,#1250,#1251,#1252,#1253,#1254,#1255,#1256,#1257,#1258,#1259,#1260,+#1261,#1262,#1263,#1264,#1265,#1266,#1267,#1268,#1269,#1270,#1271,#1272,+#1273,#1274,#1275,#1276,#1277,#1278,#1279,#1280,#1281,#1282,#1283,#1284,+#1285,#1286,#1287,#1288,#1289,#1290,#1291),.UNSPECIFIED.,.F.,.F.,(4,3,3,+3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4),(0.,0.0502534832476538,+0.06666666666667,0.120253593186299,0.1333333333333,0.2,0.255749513845402,+0.2666666666667,0.329367686983086,0.3333333333333,0.395836562108083,0.4,+0.436524911771876,0.452451414206895,0.4666666666667,0.515622988915972,0.5333333333333,+0.592226745628581,0.6,0.6666666666667,0.7333333333333,0.780353991035205,+0.8,0.852291238583784,0.8666666666667,0.911018075852901,0.9333333333333,+0.986479892247513,1.),.UNSPECIFIED.);+#46=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1293,#1294,#1295,#1296,#1297,#1298,+#1299,#1300,#1301,#1302,#1303,#1304,#1305,#1306,#1307,#1308,#1309,#1310,+#1311,#1312,#1313,#1314,#1315,#1316,#1317,#1318,#1319,#1320,#1321,#1322,+#1323,#1324,#1325,#1326,#1327,#1328,#1329,#1330,#1331,#1332,#1333,#1334,+#1335,#1336,#1337,#1338,#1339,#1340,#1341,#1342,#1343,#1344,#1345,#1346,+#1347,#1348,#1349,#1350,#1351,#1352,#1353,#1354,#1355,#1356,#1357,#1358,+#1359,#1360,#1361,#1362,#1363,#1364,#1365),.UNSPECIFIED.,.F.,.F.,(4,3,3,+3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4),(0.,0.04588956397002,0.06666666666667,+0.111096932061863,0.1333333333333,0.187622258209603,0.2,0.2666666666667,+0.315146604896394,0.3333333333333,0.4,0.4666666666667,0.500057216242827,+0.5333333333333,0.6,0.6666666666667,0.715062394803326,0.7333333333333,0.8,+0.853040552252666,0.8666666666667,0.910029166730225,0.9333333333333,0.97877240463691,+1.),.UNSPECIFIED.);+#47=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1415,#1416,#1417,#1418,#1419,#1420,+#1421,#1422,#1423,#1424,#1425,#1426,#1427,#1428,#1429,#1430,#1431,#1432,+#1433,#1434,#1435,#1436,#1437,#1438,#1439,#1440,#1441,#1442,#1443,#1444,+#1445),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,4),(0.,0.117602851245036,+0.1428571428571,0.2857142857143,0.4285714285714,0.5714285714286,0.7142857142857,+0.837110761938758,0.8571428571429,0.968740340808622,1.),.UNSPECIFIED.);+#48=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1448,#1449,#1450,#1451,#1452,#1453,+#1454,#1455,#1456,#1457,#1458,#1459,#1460,#1461,#1462,#1463,#1464,#1465,+#1466,#1467,#1468,#1469,#1470,#1471,#1472,#1473,#1474,#1475,#1476,#1477,+#1478),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,4),(0.,0.111620281731597,+0.1428571428571,0.265837725143241,0.2857142857143,0.4285714285714,0.5714285714286,+0.7142857142857,0.8571428571429,0.974754997804418,1.),.UNSPECIFIED.);+#49=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1480,#1481,#1482,#1483,#1484,#1485,+#1486,#1487,#1488,#1489,#1490,#1491,#1492,#1493,#1494,#1495,#1496,#1497,+#1498,#1499,#1500,#1501,#1502,#1503,#1504,#1505,#1506,#1507,#1508,#1509,+#1510,#1511,#1512,#1513),.UNSPECIFIED.,.F.,.F.,(4,3,3,3,3,3,3,3,3,3,3,4),+(0.,0.110670393652087,0.1428571428571,0.266027911825212,0.2857142857143,+0.4285714285714,0.555914630849822,0.5714285714286,0.7142857142857,0.8571428571429,+0.972337169518356,1.),.UNSPECIFIED.);+#50=B_SPLINE_CURVE_WITH_KNOTS('',3,(#1547,#1548,#1549,#1550,#1551,#1552,+#1553,#1554,#1555,#1556,#1557,#1558,#1559,#1560,#1561,#1562,#1563,#1564,+#1565,#1566,#1567,#1568,#1569,#1570,#1571,#1572,#1573,#1574,#1575,#1576,+#1577,#1578,#1579,#1580,#1581,#1582,#1583,#1584,#1585,#1586,#1587,#1588,+#1589,#1590,#1591,#1592,#1593,#1594,#1595,#1596,#1597,#1598,#1599,#1600,+#1601,#1602,#1603,#1604,#1605,#1606,#1607,#1608,#1609,#1610,#1611,#1612,+#1613,#1614,#1615,#1616,#1617,#1618,#1619,#1620,#1621,#1622,#1623,#1624,+#1625,#1626,#1627,#1628,#1629,#1630,#1631),.UNSPECIFIED.,.F.,.F.,(4,3,3,+3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4),(0.,0.0502991796550745,+0.06666666666667,0.120366811112814,0.1333333333333,0.2,0.255766519209152,+0.2666666666667,0.32947887253912,0.3333333333333,0.396068147196848,0.4,+0.436656907057471,0.452683676537785,0.4666666666667,0.515757169029327,0.5333333333333,+0.592124003931906,0.6,0.6666666666667,0.7333333333333,0.780356675190033,+0.8,0.852111141982603,0.8666666666667,0.911050384152092,0.9333333333333,+0.986496853314226,1.),.UNSPECIFIED.);+#51=(+BOUNDED_SURFACE()+B_SPLINE_SURFACE(2,2,((#854,#855,#856,#857,#858),(#859,#860,#861,#862,#863),+(#864,#865,#866,#867,#868)),.UNSPECIFIED.,.F.,.F.,.F.)+B_SPLINE_SURFACE_WITH_KNOTS((3,3),(3,2,3),(-0.163922449712767,-4.36520356989735E-9),+(-3.14159265417117,-1.5707963267949,5.81380277253629E-10),.UNSPECIFIED.)+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_SURFACE(((1.,0.707106780980999,1.,0.707106780980999,1.),+(0.996643058847661,0.704733065128826,0.996643058847661,0.704733065128826,+0.996643058847661),(1.,0.707106780980999,1.,0.707106780980999,1.)))+REPRESENTATION_ITEM('')+SURFACE()+);+#52=(+BOUNDED_SURFACE()+B_SPLINE_SURFACE(2,2,((#1394,#1395,#1396,#1397,#1398),(#1399,#1400,#1401,+#1402,#1403),(#1404,#1405,#1406,#1407,#1408)),.UNSPECIFIED.,.F.,.F.,.F.)+B_SPLINE_SURFACE_WITH_KNOTS((3,3),(3,2,3),(-0.140686961663467,-2.71515528509551E-9),+(-6.28318530778024,-4.71238898038469,-3.14159265298914),.UNSPECIFIED.)+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_SURFACE(((1.,0.707106780974184,1.,0.707106780974184,1.),+(0.997526917476737,0.705358047552076,0.997526917476737,0.705358047552076,+0.997526917476737),(1.,0.707106780974184,1.,0.707106780974184,1.)))+REPRESENTATION_ITEM('')+SURFACE()+);+#53=(+BOUNDED_SURFACE()+B_SPLINE_SURFACE(2,2,((#1515,#1516,#1517,#1518,#1519),(#1520,#1521,#1522,+#1523,#1524),(#1525,#1526,#1527,#1528,#1529)),.UNSPECIFIED.,.F.,.F.,.F.)+B_SPLINE_SURFACE_WITH_KNOTS((3,3),(3,2,3),(-0.163922449712767,-4.36520356989735E-9),+(-6.28318530776097,-4.71238898038469,-3.14159265300841),.UNSPECIFIED.)+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_SURFACE(((1.,0.707106780980999,1.,0.707106780980999,1.),+(0.99664305884761,0.70473306512879,0.99664305884761,0.70473306512879,0.99664305884761),+(1.,0.707106780980999,1.,0.707106780980999,1.)))+REPRESENTATION_ITEM('')+SURFACE()+);+#54=(+BOUNDED_SURFACE()+B_SPLINE_SURFACE(2,2,((#1531,#1532,#1533,#1534,#1535),(#1536,#1537,#1538,+#1539,#1540),(#1541,#1542,#1543,#1544,#1545)),.UNSPECIFIED.,.F.,.F.,.F.)+B_SPLINE_SURFACE_WITH_KNOTS((3,3),(3,2,3),(-0.140686961663467,-2.71515528509551E-9),+(-3.14159265419045,-1.5707963267949,6.00655525317961E-10),.UNSPECIFIED.)+GEOMETRIC_REPRESENTATION_ITEM()+RATIONAL_B_SPLINE_SURFACE(((1.,0.707106780974184,1.,0.707106780974184,1.),+(0.997526917476699,0.705358047552049,0.997526917476699,0.705358047552049,+0.997526917476699),(1.,0.707106780974184,1.,0.707106780974184,1.)))+REPRESENTATION_ITEM('')+SURFACE()+);+#55=LINE('',#853,#89);+#56=LINE('',#1026,#90);+#57=LINE('',#1030,#91);+#58=LINE('',#1035,#92);+#59=LINE('',#1039,#93);+#60=LINE('',#1048,#94);+#61=LINE('',#1052,#95);+#62=LINE('',#1057,#96);+#63=LINE('',#1060,#97);+#64=LINE('',#1064,#98);+#65=LINE('',#1100,#99);+#66=LINE('',#1170,#100);+#67=LINE('',#1171,#101);+#68=LINE('',#1174,#102);+#69=LINE('',#1177,#103);+#70=LINE('',#1181,#104);+#71=LINE('',#1184,#105);+#72=LINE('',#1186,#106);+#73=LINE('',#1190,#107);+#74=LINE('',#1193,#108);+#75=LINE('',#1197,#109);+#76=LINE('',#1198,#110);+#77=LINE('',#1203,#111);+#78=LINE('',#1368,#112);+#79=LINE('',#1374,#113);+#80=LINE('',#1376,#114);+#81=LINE('',#1383,#115);+#82=LINE('',#1390,#116);+#83=LINE('',#1392,#117);+#84=LINE('',#1447,#118);+#85=LINE('',#1479,#119);+#86=LINE('',#1633,#120);+#87=LINE('',#1634,#121);+#88=LINE('',#1638,#122);+#89=VECTOR('',#682,0.435);+#90=VECTOR('',#697,1.07057740755999);+#91=VECTOR('',#700,1.04731117564899);+#92=VECTOR('',#705,1.04731117564899);+#93=VECTOR('',#708,1.07057740755999);+#94=VECTOR('',#717,1.07057740755999);+#95=VECTOR('',#720,1.04731117564899);+#96=VECTOR('',#725,1.04731117564899);+#97=VECTOR('',#728,1.07057740755999);+#98=VECTOR('',#733,10.);+#99=VECTOR('',#734,10.);+#100=VECTOR('',#741,1.07364424125699);+#101=VECTOR('',#742,0.100997868637703);+#102=VECTOR('',#745,0.100997868637703);+#103=VECTOR('',#748,0.100997868637703);+#104=VECTOR('',#753,0.100997868637703);+#105=VECTOR('',#756,0.100997868637703);+#106=VECTOR('',#757,0.100997868637703);+#107=VECTOR('',#762,0.100997868637703);+#108=VECTOR('',#765,0.100997868637703);+#109=VECTOR('',#768,10.);+#110=VECTOR('',#769,1.07364424125699);+#111=VECTOR('',#774,10.);+#112=VECTOR('',#783,1.07364424125699);+#113=VECTOR('',#792,1.07364424125699);+#114=VECTOR('',#793,1.07364424125699);+#115=VECTOR('',#802,1.07364424125699);+#116=VECTOR('',#809,1.07364424125699);+#117=VECTOR('',#810,1.07364424125699);+#118=VECTOR('',#821,10.);+#119=VECTOR('',#822,10.);+#120=VECTOR('',#831,10.);+#121=VECTOR('',#832,10.);+#122=VECTOR('',#839,0.335);+#123=CYLINDRICAL_SURFACE('',#602,0.435);+#124=CYLINDRICAL_SURFACE('',#666,0.335);+#125=FACE_BOUND('',#158,.T.);+#126=FACE_OUTER_BOUND('',#157,.T.);+#127=FACE_OUTER_BOUND('',#159,.T.);+#128=FACE_OUTER_BOUND('',#160,.T.);+#129=FACE_OUTER_BOUND('',#161,.T.);+#130=FACE_OUTER_BOUND('',#162,.T.);+#131=FACE_OUTER_BOUND('',#163,.T.);+#132=FACE_OUTER_BOUND('',#164,.T.);+#133=FACE_OUTER_BOUND('',#165,.T.);+#134=FACE_OUTER_BOUND('',#166,.T.);+#135=FACE_OUTER_BOUND('',#167,.T.);+#136=FACE_OUTER_BOUND('',#168,.T.);+#137=FACE_OUTER_BOUND('',#169,.T.);+#138=FACE_OUTER_BOUND('',#170,.T.);+#139=FACE_OUTER_BOUND('',#171,.T.);+#140=FACE_OUTER_BOUND('',#172,.T.);+#141=FACE_OUTER_BOUND('',#173,.T.);+#142=FACE_OUTER_BOUND('',#174,.T.);+#143=FACE_OUTER_BOUND('',#175,.T.);+#144=FACE_OUTER_BOUND('',#176,.T.);+#145=FACE_OUTER_BOUND('',#177,.T.);+#146=FACE_OUTER_BOUND('',#178,.T.);+#147=FACE_OUTER_BOUND('',#179,.T.);+#148=FACE_OUTER_BOUND('',#180,.T.);+#149=FACE_OUTER_BOUND('',#181,.T.);+#150=FACE_OUTER_BOUND('',#182,.T.);+#151=FACE_OUTER_BOUND('',#183,.T.);+#152=FACE_OUTER_BOUND('',#184,.T.);+#153=FACE_OUTER_BOUND('',#185,.T.);+#154=FACE_OUTER_BOUND('',#186,.T.);+#155=FACE_OUTER_BOUND('',#187,.T.);+#156=FACE_OUTER_BOUND('',#188,.T.);+#157=EDGE_LOOP('',(#365));+#158=EDGE_LOOP('',(#366));+#159=EDGE_LOOP('',(#367,#368,#369,#370,#371));+#160=EDGE_LOOP('',(#372,#373,#374,#375,#376,#377,#378,#379,#380,#381));+#161=EDGE_LOOP('',(#382,#383,#384,#385,#386));+#162=EDGE_LOOP('',(#387,#388,#389,#390,#391));+#163=EDGE_LOOP('',(#392,#393,#394,#395,#396));+#164=EDGE_LOOP('',(#397,#398,#399,#400,#401));+#165=EDGE_LOOP('',(#402,#403,#404,#405,#406,#407,#408,#409,#410,#411));+#166=EDGE_LOOP('',(#412,#413,#414,#415));+#167=EDGE_LOOP('',(#416,#417,#418,#419));+#168=EDGE_LOOP('',(#420,#421,#422,#423));+#169=EDGE_LOOP('',(#424,#425,#426,#427));+#170=EDGE_LOOP('',(#428,#429,#430,#431));+#171=EDGE_LOOP('',(#432,#433,#434,#435,#436));+#172=EDGE_LOOP('',(#437,#438,#439,#440,#441,#442));+#173=EDGE_LOOP('',(#443,#444,#445,#446,#447));+#174=EDGE_LOOP('',(#448,#449,#450,#451,#452,#453));+#175=EDGE_LOOP('',(#454,#455,#456,#457,#458,#459));+#176=EDGE_LOOP('',(#460,#461,#462,#463,#464,#465));+#177=EDGE_LOOP('',(#466,#467,#468,#469,#470,#471));+#178=EDGE_LOOP('',(#472,#473,#474,#475,#476,#477,#478,#479));+#179=EDGE_LOOP('',(#480,#481,#482,#483,#484,#485,#486,#487,#488,#489));+#180=EDGE_LOOP('',(#490,#491,#492,#493));+#181=EDGE_LOOP('',(#494,#495,#496,#497,#498,#499,#500,#501,#502,#503));+#182=EDGE_LOOP('',(#504,#505,#506,#507));+#183=EDGE_LOOP('',(#508,#509,#510,#511,#512,#513,#514,#515));+#184=EDGE_LOOP('',(#516,#517,#518,#519,#520,#521));+#185=EDGE_LOOP('',(#522,#523,#524,#525,#526));+#186=EDGE_LOOP('',(#527,#528,#529,#530,#531));+#187=EDGE_LOOP('',(#532,#533,#534,#535,#536));+#188=EDGE_LOOP('',(#537,#538,#539,#540));+#189=CIRCLE('',#600,0.435);+#190=CIRCLE('',#601,0.335);+#191=CIRCLE('',#603,0.435);+#192=CIRCLE('',#604,0.435);+#193=CIRCLE('',#605,0.4251199040021);+#194=CIRCLE('',#606,1.);+#195=CIRCLE('',#607,1.);+#196=CIRCLE('',#608,0.4251199040021);+#197=CIRCLE('',#609,1.);+#198=CIRCLE('',#610,1.);+#199=CIRCLE('',#612,0.275);+#200=CIRCLE('',#615,0.275);+#201=CIRCLE('',#616,0.4251199040021);+#202=CIRCLE('',#619,0.4251199040021);+#203=CIRCLE('',#620,0.275);+#204=CIRCLE('',#623,0.275);+#205=CIRCLE('',#630,0.1740021313623);+#206=CIRCLE('',#632,0.1740021313623);+#207=CIRCLE('',#634,0.1740021313623);+#208=CIRCLE('',#636,0.1740021313623);+#209=CIRCLE('',#638,1.);+#210=CIRCLE('',#643,1.);+#211=CIRCLE('',#645,0.3261079136019);+#212=CIRCLE('',#647,0.3261079136019);+#213=CIRCLE('',#650,0.3261079136019);+#214=CIRCLE('',#653,0.3261079136019);+#215=CIRCLE('',#656,0.9);+#216=CIRCLE('',#657,0.335);+#217=CIRCLE('',#658,0.9);+#218=CIRCLE('',#662,0.335);+#219=VERTEX_POINT('',#844);+#220=VERTEX_POINT('',#846);+#221=VERTEX_POINT('',#849);+#222=VERTEX_POINT('',#850);+#223=VERTEX_POINT('',#869);+#224=VERTEX_POINT('',#870);+#225=VERTEX_POINT('',#872);+#226=VERTEX_POINT('',#874);+#227=VERTEX_POINT('',#948);+#228=VERTEX_POINT('',#950);+#229=VERTEX_POINT('',#952);+#230=VERTEX_POINT('',#988);+#231=VERTEX_POINT('',#1025);+#232=VERTEX_POINT('',#1027);+#233=VERTEX_POINT('',#1029);+#234=VERTEX_POINT('',#1033);+#235=VERTEX_POINT('',#1034);+#236=VERTEX_POINT('',#1036);+#237=VERTEX_POINT('',#1038);+#238=VERTEX_POINT('',#1040);+#239=VERTEX_POINT('',#1044);+#240=VERTEX_POINT('',#1045);+#241=VERTEX_POINT('',#1047);+#242=VERTEX_POINT('',#1049);+#243=VERTEX_POINT('',#1051);+#244=VERTEX_POINT('',#1055);+#245=VERTEX_POINT('',#1056);+#246=VERTEX_POINT('',#1058);+#247=VERTEX_POINT('',#1063);+#248=VERTEX_POINT('',#1099);+#249=VERTEX_POINT('',#1101);+#250=VERTEX_POINT('',#1103);+#251=VERTEX_POINT('',#1135);+#252=VERTEX_POINT('',#1169);+#253=VERTEX_POINT('',#1173);+#254=VERTEX_POINT('',#1175);+#255=VERTEX_POINT('',#1179);+#256=VERTEX_POINT('',#1183);+#257=VERTEX_POINT('',#1185);+#258=VERTEX_POINT('',#1189);+#259=VERTEX_POINT('',#1191);+#260=VERTEX_POINT('',#1195);+#261=VERTEX_POINT('',#1196);+#262=VERTEX_POINT('',#1201);+#263=VERTEX_POINT('',#1202);+#264=VERTEX_POINT('',#1204);+#265=VERTEX_POINT('',#1206);+#266=VERTEX_POINT('',#1372);+#267=VERTEX_POINT('',#1375);+#268=VERTEX_POINT('',#1379);+#269=VERTEX_POINT('',#1381);+#270=VERTEX_POINT('',#1385);+#271=VERTEX_POINT('',#1386);+#272=VERTEX_POINT('',#1388);+#273=VERTEX_POINT('',#1391);+#274=VERTEX_POINT('',#1409);+#275=VERTEX_POINT('',#1411);+#276=VERTEX_POINT('',#1413);+#277=EDGE_CURVE('',#219,#219,#189,.T.);+#278=EDGE_CURVE('',#220,#220,#190,.T.);+#279=EDGE_CURVE('',#221,#222,#191,.T.);+#280=EDGE_CURVE('',#221,#222,#192,.T.);+#281=EDGE_CURVE('',#222,#219,#55,.T.);+#282=EDGE_CURVE('',#223,#224,#193,.T.);+#283=EDGE_CURVE('',#225,#223,#194,.T.);+#284=EDGE_CURVE('',#225,#226,#39,.T.);+#285=EDGE_CURVE('',#226,#227,#195,.T.);+#286=EDGE_CURVE('',#228,#227,#196,.T.);+#287=EDGE_CURVE('',#228,#229,#40,.T.);+#288=EDGE_CURVE('',#221,#229,#197,.T.);+#289=EDGE_CURVE('',#222,#230,#198,.T.);+#290=EDGE_CURVE('',#230,#224,#41,.T.);+#291=EDGE_CURVE('',#227,#231,#56,.T.);+#292=EDGE_CURVE('',#232,#231,#199,.T.);+#293=EDGE_CURVE('',#233,#232,#57,.T.);+#294=EDGE_CURVE('',#233,#228,#15,.T.);+#295=EDGE_CURVE('',#234,#235,#58,.T.);+#296=EDGE_CURVE('',#236,#235,#200,.T.);+#297=EDGE_CURVE('',#237,#236,#59,.T.);+#298=EDGE_CURVE('',#237,#238,#201,.T.);+#299=EDGE_CURVE('',#238,#234,#16,.T.);+#300=EDGE_CURVE('',#239,#240,#202,.T.);+#301=EDGE_CURVE('',#240,#241,#60,.T.);+#302=EDGE_CURVE('',#242,#241,#203,.T.);+#303=EDGE_CURVE('',#243,#242,#61,.T.);+#304=EDGE_CURVE('',#243,#239,#17,.T.);+#305=EDGE_CURVE('',#244,#245,#62,.T.);+#306=EDGE_CURVE('',#246,#245,#204,.T.);+#307=EDGE_CURVE('',#223,#246,#63,.T.);+#308=EDGE_CURVE('',#224,#244,#18,.T.);+#309=EDGE_CURVE('',#247,#234,#64,.T.);+#310=EDGE_CURVE('',#229,#238,#42,.T.);+#311=EDGE_CURVE('',#248,#233,#65,.T.);+#312=EDGE_CURVE('',#249,#248,#19,.T.);+#313=EDGE_CURVE('',#250,#249,#43,.T.);+#314=EDGE_CURVE('',#251,#250,#44,.T.);+#315=EDGE_CURVE('',#247,#251,#20,.T.);+#316=EDGE_CURVE('',#252,#247,#66,.T.);+#317=EDGE_CURVE('',#252,#235,#67,.T.);+#318=EDGE_CURVE('',#253,#241,#68,.T.);+#319=EDGE_CURVE('',#253,#254,#205,.T.);+#320=EDGE_CURVE('',#242,#254,#69,.T.);+#321=EDGE_CURVE('',#252,#255,#206,.T.);+#322=EDGE_CURVE('',#236,#255,#70,.T.);+#323=EDGE_CURVE('',#246,#256,#71,.T.);+#324=EDGE_CURVE('',#257,#245,#72,.T.);+#325=EDGE_CURVE('',#257,#256,#207,.T.);+#326=EDGE_CURVE('',#258,#231,#73,.T.);+#327=EDGE_CURVE('',#258,#259,#208,.T.);+#328=EDGE_CURVE('',#232,#259,#74,.T.);+#329=EDGE_CURVE('',#260,#261,#75,.T.);+#330=EDGE_CURVE('',#253,#261,#76,.T.);+#331=EDGE_CURVE('',#260,#240,#209,.T.);+#332=EDGE_CURVE('',#262,#263,#77,.T.);+#333=EDGE_CURVE('',#264,#263,#21,.T.);+#334=EDGE_CURVE('',#265,#264,#45,.T.);+#335=EDGE_CURVE('',#261,#265,#22,.T.);+#336=EDGE_CURVE('',#262,#260,#46,.T.);+#337=EDGE_CURVE('',#262,#237,#210,.T.);+#338=EDGE_CURVE('',#263,#255,#78,.T.);+#339=EDGE_CURVE('',#251,#264,#211,.T.);+#340=EDGE_CURVE('',#266,#249,#212,.T.);+#341=EDGE_CURVE('',#248,#259,#79,.T.);+#342=EDGE_CURVE('',#258,#267,#80,.T.);+#343=EDGE_CURVE('',#267,#266,#23,.T.);+#344=EDGE_CURVE('',#265,#268,#213,.T.);+#345=EDGE_CURVE('',#268,#269,#24,.T.);+#346=EDGE_CURVE('',#269,#254,#81,.T.);+#347=EDGE_CURVE('',#270,#271,#214,.T.);+#348=EDGE_CURVE('',#271,#272,#25,.T.);+#349=EDGE_CURVE('',#272,#256,#82,.T.);+#350=EDGE_CURVE('',#257,#273,#83,.T.);+#351=EDGE_CURVE('',#273,#270,#26,.T.);+#352=EDGE_CURVE('',#274,#250,#215,.T.);+#353=EDGE_CURVE('',#274,#275,#216,.T.);+#354=EDGE_CURVE('',#275,#276,#217,.T.);+#355=EDGE_CURVE('',#276,#268,#47,.T.);+#356=EDGE_CURVE('',#243,#269,#84,.T.);+#357=EDGE_CURVE('',#270,#276,#48,.T.);+#358=EDGE_CURVE('',#244,#273,#85,.T.);+#359=EDGE_CURVE('',#239,#230,#49,.T.);+#360=EDGE_CURVE('',#274,#275,#218,.T.);+#361=EDGE_CURVE('',#266,#271,#50,.T.);+#362=EDGE_CURVE('',#225,#272,#86,.T.);+#363=EDGE_CURVE('',#226,#267,#87,.T.);+#364=EDGE_CURVE('',#220,#275,#88,.T.);+#365=ORIENTED_EDGE('',*,*,#277,.T.);+#366=ORIENTED_EDGE('',*,*,#278,.T.);+#367=ORIENTED_EDGE('',*,*,#279,.F.);+#368=ORIENTED_EDGE('',*,*,#280,.T.);+#369=ORIENTED_EDGE('',*,*,#281,.T.);+#370=ORIENTED_EDGE('',*,*,#277,.F.);+#371=ORIENTED_EDGE('',*,*,#281,.F.);+#372=ORIENTED_EDGE('',*,*,#282,.F.);+#373=ORIENTED_EDGE('',*,*,#283,.F.);+#374=ORIENTED_EDGE('',*,*,#284,.T.);+#375=ORIENTED_EDGE('',*,*,#285,.T.);+#376=ORIENTED_EDGE('',*,*,#286,.F.);+#377=ORIENTED_EDGE('',*,*,#287,.T.);+#378=ORIENTED_EDGE('',*,*,#288,.F.);+#379=ORIENTED_EDGE('',*,*,#279,.T.);+#380=ORIENTED_EDGE('',*,*,#289,.T.);+#381=ORIENTED_EDGE('',*,*,#290,.T.);+#382=ORIENTED_EDGE('',*,*,#286,.T.);+#383=ORIENTED_EDGE('',*,*,#291,.T.);+#384=ORIENTED_EDGE('',*,*,#292,.F.);+#385=ORIENTED_EDGE('',*,*,#293,.F.);+#386=ORIENTED_EDGE('',*,*,#294,.T.);+#387=ORIENTED_EDGE('',*,*,#295,.T.);+#388=ORIENTED_EDGE('',*,*,#296,.F.);+#389=ORIENTED_EDGE('',*,*,#297,.F.);+#390=ORIENTED_EDGE('',*,*,#298,.T.);+#391=ORIENTED_EDGE('',*,*,#299,.T.);+#392=ORIENTED_EDGE('',*,*,#300,.T.);+#393=ORIENTED_EDGE('',*,*,#301,.T.);+#394=ORIENTED_EDGE('',*,*,#302,.F.);+#395=ORIENTED_EDGE('',*,*,#303,.F.);+#396=ORIENTED_EDGE('',*,*,#304,.T.);+#397=ORIENTED_EDGE('',*,*,#305,.T.);+#398=ORIENTED_EDGE('',*,*,#306,.F.);+#399=ORIENTED_EDGE('',*,*,#307,.F.);+#400=ORIENTED_EDGE('',*,*,#282,.T.);+#401=ORIENTED_EDGE('',*,*,#308,.T.);+#402=ORIENTED_EDGE('',*,*,#309,.T.);+#403=ORIENTED_EDGE('',*,*,#299,.F.);+#404=ORIENTED_EDGE('',*,*,#310,.F.);+#405=ORIENTED_EDGE('',*,*,#287,.F.);+#406=ORIENTED_EDGE('',*,*,#294,.F.);+#407=ORIENTED_EDGE('',*,*,#311,.F.);+#408=ORIENTED_EDGE('',*,*,#312,.F.);+#409=ORIENTED_EDGE('',*,*,#313,.F.);+#410=ORIENTED_EDGE('',*,*,#314,.F.);+#411=ORIENTED_EDGE('',*,*,#315,.F.);+#412=ORIENTED_EDGE('',*,*,#309,.F.);+#413=ORIENTED_EDGE('',*,*,#316,.F.);+#414=ORIENTED_EDGE('',*,*,#317,.T.);+#415=ORIENTED_EDGE('',*,*,#295,.F.);+#416=ORIENTED_EDGE('',*,*,#318,.F.);+#417=ORIENTED_EDGE('',*,*,#319,.T.);+#418=ORIENTED_EDGE('',*,*,#320,.F.);+#419=ORIENTED_EDGE('',*,*,#302,.T.);+#420=ORIENTED_EDGE('',*,*,#317,.F.);+#421=ORIENTED_EDGE('',*,*,#321,.T.);+#422=ORIENTED_EDGE('',*,*,#322,.F.);+#423=ORIENTED_EDGE('',*,*,#296,.T.);+#424=ORIENTED_EDGE('',*,*,#323,.F.);+#425=ORIENTED_EDGE('',*,*,#306,.T.);+#426=ORIENTED_EDGE('',*,*,#324,.F.);+#427=ORIENTED_EDGE('',*,*,#325,.T.);+#428=ORIENTED_EDGE('',*,*,#326,.F.);+#429=ORIENTED_EDGE('',*,*,#327,.T.);+#430=ORIENTED_EDGE('',*,*,#328,.F.);+#431=ORIENTED_EDGE('',*,*,#292,.T.);+#432=ORIENTED_EDGE('',*,*,#329,.T.);+#433=ORIENTED_EDGE('',*,*,#330,.F.);+#434=ORIENTED_EDGE('',*,*,#318,.T.);+#435=ORIENTED_EDGE('',*,*,#301,.F.);+#436=ORIENTED_EDGE('',*,*,#331,.F.);+#437=ORIENTED_EDGE('',*,*,#332,.T.);+#438=ORIENTED_EDGE('',*,*,#333,.F.);+#439=ORIENTED_EDGE('',*,*,#334,.F.);+#440=ORIENTED_EDGE('',*,*,#335,.F.);+#441=ORIENTED_EDGE('',*,*,#329,.F.);+#442=ORIENTED_EDGE('',*,*,#336,.F.);+#443=ORIENTED_EDGE('',*,*,#332,.F.);+#444=ORIENTED_EDGE('',*,*,#337,.T.);+#445=ORIENTED_EDGE('',*,*,#297,.T.);+#446=ORIENTED_EDGE('',*,*,#322,.T.);+#447=ORIENTED_EDGE('',*,*,#338,.F.);+#448=ORIENTED_EDGE('',*,*,#338,.T.);+#449=ORIENTED_EDGE('',*,*,#321,.F.);+#450=ORIENTED_EDGE('',*,*,#316,.T.);+#451=ORIENTED_EDGE('',*,*,#315,.T.);+#452=ORIENTED_EDGE('',*,*,#339,.T.);+#453=ORIENTED_EDGE('',*,*,#333,.T.);+#454=ORIENTED_EDGE('',*,*,#340,.T.);+#455=ORIENTED_EDGE('',*,*,#312,.T.);+#456=ORIENTED_EDGE('',*,*,#341,.T.);+#457=ORIENTED_EDGE('',*,*,#327,.F.);+#458=ORIENTED_EDGE('',*,*,#342,.T.);+#459=ORIENTED_EDGE('',*,*,#343,.T.);+#460=ORIENTED_EDGE('',*,*,#344,.T.);+#461=ORIENTED_EDGE('',*,*,#345,.T.);+#462=ORIENTED_EDGE('',*,*,#346,.T.);+#463=ORIENTED_EDGE('',*,*,#319,.F.);+#464=ORIENTED_EDGE('',*,*,#330,.T.);+#465=ORIENTED_EDGE('',*,*,#335,.T.);+#466=ORIENTED_EDGE('',*,*,#347,.T.);+#467=ORIENTED_EDGE('',*,*,#348,.T.);+#468=ORIENTED_EDGE('',*,*,#349,.T.);+#469=ORIENTED_EDGE('',*,*,#325,.F.);+#470=ORIENTED_EDGE('',*,*,#350,.T.);+#471=ORIENTED_EDGE('',*,*,#351,.T.);+#472=ORIENTED_EDGE('',*,*,#339,.F.);+#473=ORIENTED_EDGE('',*,*,#314,.T.);+#474=ORIENTED_EDGE('',*,*,#352,.F.);+#475=ORIENTED_EDGE('',*,*,#353,.T.);+#476=ORIENTED_EDGE('',*,*,#354,.T.);+#477=ORIENTED_EDGE('',*,*,#355,.T.);+#478=ORIENTED_EDGE('',*,*,#344,.F.);+#479=ORIENTED_EDGE('',*,*,#334,.T.);+#480=ORIENTED_EDGE('',*,*,#356,.T.);+#481=ORIENTED_EDGE('',*,*,#345,.F.);+#482=ORIENTED_EDGE('',*,*,#355,.F.);+#483=ORIENTED_EDGE('',*,*,#357,.F.);+#484=ORIENTED_EDGE('',*,*,#351,.F.);+#485=ORIENTED_EDGE('',*,*,#358,.F.);+#486=ORIENTED_EDGE('',*,*,#308,.F.);+#487=ORIENTED_EDGE('',*,*,#290,.F.);+#488=ORIENTED_EDGE('',*,*,#359,.F.);+#489=ORIENTED_EDGE('',*,*,#304,.F.);+#490=ORIENTED_EDGE('',*,*,#356,.F.);+#491=ORIENTED_EDGE('',*,*,#303,.T.);+#492=ORIENTED_EDGE('',*,*,#320,.T.);+#493=ORIENTED_EDGE('',*,*,#346,.F.);+#494=ORIENTED_EDGE('',*,*,#300,.F.);+#495=ORIENTED_EDGE('',*,*,#359,.T.);+#496=ORIENTED_EDGE('',*,*,#289,.F.);+#497=ORIENTED_EDGE('',*,*,#280,.F.);+#498=ORIENTED_EDGE('',*,*,#288,.T.);+#499=ORIENTED_EDGE('',*,*,#310,.T.);+#500=ORIENTED_EDGE('',*,*,#298,.F.);+#501=ORIENTED_EDGE('',*,*,#337,.F.);+#502=ORIENTED_EDGE('',*,*,#336,.T.);+#503=ORIENTED_EDGE('',*,*,#331,.T.);+#504=ORIENTED_EDGE('',*,*,#358,.T.);+#505=ORIENTED_EDGE('',*,*,#350,.F.);+#506=ORIENTED_EDGE('',*,*,#324,.T.);+#507=ORIENTED_EDGE('',*,*,#305,.F.);+#508=ORIENTED_EDGE('',*,*,#354,.F.);+#509=ORIENTED_EDGE('',*,*,#360,.F.);+#510=ORIENTED_EDGE('',*,*,#352,.T.);+#511=ORIENTED_EDGE('',*,*,#313,.T.);+#512=ORIENTED_EDGE('',*,*,#340,.F.);+#513=ORIENTED_EDGE('',*,*,#361,.T.);+#514=ORIENTED_EDGE('',*,*,#347,.F.);+#515=ORIENTED_EDGE('',*,*,#357,.T.);+#516=ORIENTED_EDGE('',*,*,#362,.T.);+#517=ORIENTED_EDGE('',*,*,#348,.F.);+#518=ORIENTED_EDGE('',*,*,#361,.F.);+#519=ORIENTED_EDGE('',*,*,#343,.F.);+#520=ORIENTED_EDGE('',*,*,#363,.F.);+#521=ORIENTED_EDGE('',*,*,#284,.F.);+#522=ORIENTED_EDGE('',*,*,#362,.F.);+#523=ORIENTED_EDGE('',*,*,#283,.T.);+#524=ORIENTED_EDGE('',*,*,#307,.T.);+#525=ORIENTED_EDGE('',*,*,#323,.T.);+#526=ORIENTED_EDGE('',*,*,#349,.F.);+#527=ORIENTED_EDGE('',*,*,#363,.T.);+#528=ORIENTED_EDGE('',*,*,#342,.F.);+#529=ORIENTED_EDGE('',*,*,#326,.T.);+#530=ORIENTED_EDGE('',*,*,#291,.F.);+#531=ORIENTED_EDGE('',*,*,#285,.F.);+#532=ORIENTED_EDGE('',*,*,#278,.F.);+#533=ORIENTED_EDGE('',*,*,#364,.T.);+#534=ORIENTED_EDGE('',*,*,#353,.F.);+#535=ORIENTED_EDGE('',*,*,#360,.T.);+#536=ORIENTED_EDGE('',*,*,#364,.F.);+#537=ORIENTED_EDGE('',*,*,#311,.T.);+#538=ORIENTED_EDGE('',*,*,#293,.T.);+#539=ORIENTED_EDGE('',*,*,#328,.T.);+#540=ORIENTED_EDGE('',*,*,#341,.F.);+#541=PLANE('',#599);+#542=PLANE('',#628);+#543=PLANE('',#629);+#544=PLANE('',#631);+#545=PLANE('',#633);+#546=PLANE('',#635);+#547=PLANE('',#637);+#548=PLANE('',#642);+#549=PLANE('',#660);+#550=PLANE('',#661);+#551=PLANE('',#664);+#552=PLANE('',#665);+#553=PLANE('',#667);+#554=ADVANCED_FACE('',(#126,#125),#541,.F.);+#555=ADVANCED_FACE('',(#127),#123,.T.);+#556=ADVANCED_FACE('',(#128),#51,.F.);+#557=ADVANCED_FACE('',(#129),#27,.T.);+#558=ADVANCED_FACE('',(#130),#28,.T.);+#559=ADVANCED_FACE('',(#131),#29,.T.);+#560=ADVANCED_FACE('',(#132),#30,.T.);+#561=ADVANCED_FACE('',(#133),#31,.F.);+#562=ADVANCED_FACE('',(#134),#542,.F.);+#563=ADVANCED_FACE('',(#135),#543,.T.);+#564=ADVANCED_FACE('',(#136),#544,.T.);+#565=ADVANCED_FACE('',(#137),#545,.T.);+#566=ADVANCED_FACE('',(#138),#546,.T.);+#567=ADVANCED_FACE('',(#139),#547,.F.);+#568=ADVANCED_FACE('',(#140),#32,.F.);+#569=ADVANCED_FACE('',(#141),#548,.F.);+#570=ADVANCED_FACE('',(#142),#33,.F.);+#571=ADVANCED_FACE('',(#143),#34,.F.);+#572=ADVANCED_FACE('',(#144),#35,.F.);+#573=ADVANCED_FACE('',(#145),#36,.F.);+#574=ADVANCED_FACE('',(#146),#52,.T.);+#575=ADVANCED_FACE('',(#147),#37,.F.);+#576=ADVANCED_FACE('',(#148),#549,.F.);+#577=ADVANCED_FACE('',(#149),#53,.F.);+#578=ADVANCED_FACE('',(#150),#550,.F.);+#579=ADVANCED_FACE('',(#151),#54,.T.);+#580=ADVANCED_FACE('',(#152),#38,.F.);+#581=ADVANCED_FACE('',(#153),#551,.F.);+#582=ADVANCED_FACE('',(#154),#552,.F.);+#583=ADVANCED_FACE('',(#155),#124,.F.);+#584=ADVANCED_FACE('',(#156),#553,.F.);+#585=CLOSED_SHELL('',(#554,#555,#556,#557,#558,#559,#560,#561,#562,#563,+#564,#565,#566,#567,#568,#569,#570,#571,#572,#573,#574,#575,#576,#577,#578,+#579,#580,#581,#582,#583,#584));+#586=DERIVED_UNIT_ELEMENT(#588,1.);+#587=DERIVED_UNIT_ELEMENT(#1645,-3.);+#588=(+MASS_UNIT()+NAMED_UNIT(*)+SI_UNIT(.KILO.,.GRAM.)+);+#589=DERIVED_UNIT((#586,#587));+#590=MEASURE_REPRESENTATION_ITEM('density measure',+POSITIVE_RATIO_MEASURE(7850.),#589);+#591=PROPERTY_DEFINITION_REPRESENTATION(#596,#593);+#592=PROPERTY_DEFINITION_REPRESENTATION(#597,#594);+#593=REPRESENTATION('material name',(#595),#1642);+#594=REPRESENTATION('density',(#590),#1642);+#595=DESCRIPTIVE_REPRESENTATION_ITEM('Steel','Steel');+#596=PROPERTY_DEFINITION('material property','material name',#1652);+#597=PROPERTY_DEFINITION('material property','density of part',#1652);+#598=AXIS2_PLACEMENT_3D('',#842,#668,#669);+#599=AXIS2_PLACEMENT_3D('',#843,#670,#671);+#600=AXIS2_PLACEMENT_3D('',#845,#672,#673);+#601=AXIS2_PLACEMENT_3D('',#847,#674,#675);+#602=AXIS2_PLACEMENT_3D('',#848,#676,#677);+#603=AXIS2_PLACEMENT_3D('',#851,#678,#679);+#604=AXIS2_PLACEMENT_3D('',#852,#680,#681);+#605=AXIS2_PLACEMENT_3D('',#871,#683,#684);+#606=AXIS2_PLACEMENT_3D('',#873,#685,#686);+#607=AXIS2_PLACEMENT_3D('',#949,#687,#688);+#608=AXIS2_PLACEMENT_3D('',#951,#689,#690);+#609=AXIS2_PLACEMENT_3D('',#987,#691,#692);+#610=AXIS2_PLACEMENT_3D('',#989,#693,#694);+#611=AXIS2_PLACEMENT_3D('',#1024,#695,#696);+#612=AXIS2_PLACEMENT_3D('',#1028,#698,#699);+#613=AXIS2_PLACEMENT_3D('',#1031,#701,#702);+#614=AXIS2_PLACEMENT_3D('',#1032,#703,#704);+#615=AXIS2_PLACEMENT_3D('',#1037,#706,#707);+#616=AXIS2_PLACEMENT_3D('',#1041,#709,#710);+#617=AXIS2_PLACEMENT_3D('',#1042,#711,#712);+#618=AXIS2_PLACEMENT_3D('',#1043,#713,#714);+#619=AXIS2_PLACEMENT_3D('',#1046,#715,#716);+#620=AXIS2_PLACEMENT_3D('',#1050,#718,#719);+#621=AXIS2_PLACEMENT_3D('',#1053,#721,#722);+#622=AXIS2_PLACEMENT_3D('',#1054,#723,#724);+#623=AXIS2_PLACEMENT_3D('',#1059,#726,#727);+#624=AXIS2_PLACEMENT_3D('',#1061,#729,#730);+#625=AXIS2_PLACEMENT_3D('',#1062,#731,#732);+#626=AXIS2_PLACEMENT_3D('',#1102,#735,#736);+#627=AXIS2_PLACEMENT_3D('',#1167,#737,#738);+#628=AXIS2_PLACEMENT_3D('',#1168,#739,#740);+#629=AXIS2_PLACEMENT_3D('',#1172,#743,#744);+#630=AXIS2_PLACEMENT_3D('',#1176,#746,#747);+#631=AXIS2_PLACEMENT_3D('',#1178,#749,#750);+#632=AXIS2_PLACEMENT_3D('',#1180,#751,#752);+#633=AXIS2_PLACEMENT_3D('',#1182,#754,#755);+#634=AXIS2_PLACEMENT_3D('',#1187,#758,#759);+#635=AXIS2_PLACEMENT_3D('',#1188,#760,#761);+#636=AXIS2_PLACEMENT_3D('',#1192,#763,#764);+#637=AXIS2_PLACEMENT_3D('',#1194,#766,#767);+#638=AXIS2_PLACEMENT_3D('',#1199,#770,#771);+#639=AXIS2_PLACEMENT_3D('',#1200,#772,#773);+#640=AXIS2_PLACEMENT_3D('',#1205,#775,#776);+#641=AXIS2_PLACEMENT_3D('',#1292,#777,#778);+#642=AXIS2_PLACEMENT_3D('',#1366,#779,#780);+#643=AXIS2_PLACEMENT_3D('',#1367,#781,#782);+#644=AXIS2_PLACEMENT_3D('',#1369,#784,#785);+#645=AXIS2_PLACEMENT_3D('',#1370,#786,#787);+#646=AXIS2_PLACEMENT_3D('',#1371,#788,#789);+#647=AXIS2_PLACEMENT_3D('',#1373,#790,#791);+#648=AXIS2_PLACEMENT_3D('',#1377,#794,#795);+#649=AXIS2_PLACEMENT_3D('',#1378,#796,#797);+#650=AXIS2_PLACEMENT_3D('',#1380,#798,#799);+#651=AXIS2_PLACEMENT_3D('',#1382,#800,#801);+#652=AXIS2_PLACEMENT_3D('',#1384,#803,#804);+#653=AXIS2_PLACEMENT_3D('',#1387,#805,#806);+#654=AXIS2_PLACEMENT_3D('',#1389,#807,#808);+#655=AXIS2_PLACEMENT_3D('',#1393,#811,#812);+#656=AXIS2_PLACEMENT_3D('',#1410,#813,#814);+#657=AXIS2_PLACEMENT_3D('',#1412,#815,#816);+#658=AXIS2_PLACEMENT_3D('',#1414,#817,#818);+#659=AXIS2_PLACEMENT_3D('',#1446,#819,#820);+#660=AXIS2_PLACEMENT_3D('',#1514,#823,#824);+#661=AXIS2_PLACEMENT_3D('',#1530,#825,#826);+#662=AXIS2_PLACEMENT_3D('',#1546,#827,#828);+#663=AXIS2_PLACEMENT_3D('',#1632,#829,#830);+#664=AXIS2_PLACEMENT_3D('',#1635,#833,#834);+#665=AXIS2_PLACEMENT_3D('',#1636,#835,#836);+#666=AXIS2_PLACEMENT_3D('',#1637,#837,#838);+#667=AXIS2_PLACEMENT_3D('',#1639,#840,#841);+#668=DIRECTION('axis',(0.,0.,1.));+#669=DIRECTION('refdir',(1.,0.,0.));+#670=DIRECTION('center_axis',(0.,0.,-1.));+#671=DIRECTION('ref_axis',(1.,0.,0.));+#672=DIRECTION('center_axis',(0.,0.,1.));+#673=DIRECTION('ref_axis',(1.,0.,0.));+#674=DIRECTION('center_axis',(0.,0.,-1.));+#675=DIRECTION('ref_axis',(1.,0.,0.));+#676=DIRECTION('center_axis',(0.,0.,-1.));+#677=DIRECTION('ref_axis',(1.,0.,0.));+#678=DIRECTION('center_axis',(0.,0.,-1.));+#679=DIRECTION('ref_axis',(1.,0.,0.));+#680=DIRECTION('center_axis',(0.,0.,1.));+#681=DIRECTION('ref_axis',(1.,0.,0.));+#682=DIRECTION('',(0.,0.,1.));+#683=DIRECTION('center_axis',(0.,0.,-1.));+#684=DIRECTION('ref_axis',(-0.258819045102493,-0.965925826289076,0.));+#685=DIRECTION('center_axis',(-0.965925826289076,0.258819045102493,0.));+#686=DIRECTION('ref_axis',(-0.25534951843075,-0.952977376471912,-0.1631892869336));+#687=DIRECTION('center_axis',(-0.965925826289076,-0.258819045102493,0.));+#688=DIRECTION('ref_axis',(0.25534951843075,-0.952977376471912,-0.1631892869336));+#689=DIRECTION('center_axis',(0.,0.,-1.));+#690=DIRECTION('ref_axis',(0.96737059980658,-0.253365590856095,0.));+#691=DIRECTION('center_axis',(0.,1.,0.));+#692=DIRECTION('ref_axis',(1.,0.,0.));+#693=DIRECTION('center_axis',(0.,-1.,0.));+#694=DIRECTION('ref_axis',(-1.,0.,0.));+#695=DIRECTION('center_axis',(0.,0.,1.));+#696=DIRECTION('ref_axis',(-1.,0.,0.));+#697=DIRECTION('',(-0.0362924623015003,0.135445313241001,-0.990119904002107));+#698=DIRECTION('center_axis',(0.,0.,-1.));+#699=DIRECTION('ref_axis',(0.965925826289076,-0.258819045102493,0.));+#700=DIRECTION('',(-0.135445313241001,0.0362924623015003,-0.990119904002107));+#701=DIRECTION('center_axis',(-0.957600163102212,0.251556339988859,0.140432672259792));+#702=DIRECTION('ref_axis',(-0.181219591627885,-0.904875323995765,0.385175424493544));+#703=DIRECTION('center_axis',(0.,0.,1.));+#704=DIRECTION('ref_axis',(-1.,0.,0.));+#705=DIRECTION('',(-0.135445313241001,-0.0362924623015003,-0.990119904002107));+#706=DIRECTION('center_axis',(0.,0.,-1.));+#707=DIRECTION('ref_axis',(0.258819045102493,0.965925826289076,0.));+#708=DIRECTION('',(-0.0362924623015003,-0.135445313241001,-0.990119904002107));+#709=DIRECTION('center_axis',(0.,0.,-1.));+#710=DIRECTION('ref_axis',(0.258819045102493,0.965925826289076,0.));+#711=DIRECTION('center_axis',(-0.957600163102295,-0.25155633998854,0.140432672259801));+#712=DIRECTION('ref_axis',(0.181219591614085,-0.904875323975133,-0.385175424548506));+#713=DIRECTION('center_axis',(0.,0.,1.));+#714=DIRECTION('ref_axis',(-1.,0.,0.));+#715=DIRECTION('center_axis',(0.,0.,-1.));+#716=DIRECTION('ref_axis',(-0.96737059980658,0.253365590856095,0.));+#717=DIRECTION('',(0.0362924623015003,-0.135445313241001,-0.990119904002107));+#718=DIRECTION('center_axis',(0.,0.,-1.));+#719=DIRECTION('ref_axis',(-0.965925826289076,0.258819045102493,0.));+#720=DIRECTION('',(0.135445313241001,-0.0362924623015003,-0.990119904002107));+#721=DIRECTION('center_axis',(0.957603304947962,-0.251543639201056,0.140433998484091));+#722=DIRECTION('ref_axis',(-0.181193095598003,-0.904856320073277,-0.3852325299492));+#723=DIRECTION('center_axis',(0.,0.,1.));+#724=DIRECTION('ref_axis',(-1.,0.,0.));+#725=DIRECTION('',(0.135445313241001,0.0362924623015003,-0.990119904002107));+#726=DIRECTION('center_axis',(0.,0.,-1.));+#727=DIRECTION('ref_axis',(-0.258819045102493,-0.965925826289076,0.));+#728=DIRECTION('',(0.0362924623015003,0.135445313241001,-0.990119904002107));+#729=DIRECTION('center_axis',(0.957603304947995,0.251543639200925,0.1404339984841));+#730=DIRECTION('ref_axis',(0.181193095549796,-0.904856319999574,0.385232530144991));+#731=DIRECTION('center_axis',(0.968100345885965,-2.26300483551748E-12,-0.250562807087314));+#732=DIRECTION('ref_axis',(0.0467510353901437,0.98243899267131,0.180632129946286));+#733=DIRECTION('',(0.933012701892336,0.250000000000174,-0.258819045101934));+#734=DIRECTION('',(0.933012701892336,-0.250000000000174,-0.258819045101934));+#735=DIRECTION('center_axis',(0.9568548906207,-0.254464714142449,-0.140272689970821));+#736=DIRECTION('ref_axis',(0.182714717762633,0.902301409453812,-0.390470867556832));+#737=DIRECTION('center_axis',(0.956854890620863,0.254464714141805,-0.140272689970877));+#738=DIRECTION('ref_axis',(-0.182714719036956,0.902301411417719,0.390470862422332));+#739=DIRECTION('center_axis',(-0.258819045102493,0.965925826289076,0.));+#740=DIRECTION('ref_axis',(0.965925826289076,0.258819045102493,0.));+#741=DIRECTION('',(0.135445313241001,0.0362924623015003,0.990119904002107));+#742=DIRECTION('',(0.965925826289076,0.258819045102493,0.));+#743=DIRECTION('center_axis',(0.,0.,-1.));+#744=DIRECTION('ref_axis',(1.,0.,0.));+#745=DIRECTION('',(-0.258819045102493,0.965925826289076,0.));+#746=DIRECTION('center_axis',(0.,0.,1.));+#747=DIRECTION('ref_axis',(-0.258819045102493,0.965925826289076,0.));+#748=DIRECTION('',(0.965925826289076,-0.258819045102493,0.));+#749=DIRECTION('center_axis',(0.,0.,-1.));+#750=DIRECTION('ref_axis',(1.,0.,0.));+#751=DIRECTION('center_axis',(0.,0.,1.));+#752=DIRECTION('ref_axis',(0.965925826289076,0.258819045102493,0.));+#753=DIRECTION('',(-0.258819045102493,-0.965925826289076,0.));+#754=DIRECTION('center_axis',(0.,0.,-1.));+#755=DIRECTION('ref_axis',(1.,0.,0.));+#756=DIRECTION('',(0.258819045102493,0.965925826289076,0.));+#757=DIRECTION('',(-0.965925826289076,-0.258819045102493,0.));+#758=DIRECTION('center_axis',(0.,0.,1.));+#759=DIRECTION('ref_axis',(-0.965925826289076,-0.258819045102493,0.));+#760=DIRECTION('center_axis',(0.,0.,-1.));+#761=DIRECTION('ref_axis',(1.,0.,0.));+#762=DIRECTION('',(0.258819045102493,-0.965925826289076,0.));+#763=DIRECTION('center_axis',(0.,0.,1.));+#764=DIRECTION('ref_axis',(0.258819045102493,-0.965925826289076,0.));+#765=DIRECTION('',(-0.965925826289076,0.258819045102493,0.));+#766=DIRECTION('center_axis',(-0.965925826289076,-0.258819045102493,0.));+#767=DIRECTION('ref_axis',(-0.258819045102493,0.965925826289076,0.));+#768=DIRECTION('',(0.250000000000431,-0.93301270189216,0.258819045102318));+#769=DIRECTION('',(-0.0362924623015003,0.135445313241001,0.990119904002107));+#770=DIRECTION('center_axis',(0.965925826289076,0.258819045102493,0.));+#771=DIRECTION('ref_axis',(-0.25534951843075,0.952977376471912,-0.1631892869336));+#772=DIRECTION('center_axis',(8.99539455035338E-12,0.968100345888207,-0.250562807078652));+#773=DIRECTION('ref_axis',(0.98243899266883,0.046751035381091,0.180632129962115));+#774=DIRECTION('',(-0.250000000000431,-0.93301270189216,0.258819045102318));+#775=DIRECTION('center_axis',(0.254457851799343,0.956856650686256,-0.140273132477836));+#776=DIRECTION('ref_axis',(0.902386042589139,-0.182762172811296,0.390253018347848));+#777=DIRECTION('center_axis',(-0.254457917399929,0.95685663386115,-0.140273128247718));+#778=DIRECTION('ref_axis',(0.902385442836804,0.182761855001197,-0.390254553993603));+#779=DIRECTION('center_axis',(0.965925826289076,-0.258819045102493,0.));+#780=DIRECTION('ref_axis',(-0.258819045102493,-0.965925826289076,0.));+#781=DIRECTION('center_axis',(0.965925826289076,-0.258819045102493,0.));+#782=DIRECTION('ref_axis',(0.25534951843075,0.952977376471912,-0.1631892869336));+#783=DIRECTION('',(-0.0362924623015003,-0.135445313241001,-0.990119904002107));+#784=DIRECTION('center_axis',(0.,0.,1.));+#785=DIRECTION('ref_axis',(-1.,0.,0.));+#786=DIRECTION('center_axis',(0.,0.,1.));+#787=DIRECTION('ref_axis',(0.966481459586918,0.256736417897305,0.));+#788=DIRECTION('center_axis',(0.,0.,1.));+#789=DIRECTION('ref_axis',(-1.,0.,0.));+#790=DIRECTION('center_axis',(0.,0.,1.));+#791=DIRECTION('ref_axis',(0.256736417897305,-0.966481459586918,0.));+#792=DIRECTION('',(-0.135445313241001,0.0362924623015003,-0.990119904002107));+#793=DIRECTION('',(0.0362924623015003,-0.135445313241001,0.990119904002107));+#794=DIRECTION('center_axis',(0.254457836707408,-0.956856654557006,-0.140273133450992));+#795=DIRECTION('ref_axis',(0.902386175738344,0.182762242790015,0.390252677693171));+#796=DIRECTION('center_axis',(0.,0.,1.));+#797=DIRECTION('ref_axis',(-1.,0.,0.));+#798=DIRECTION('center_axis',(0.,0.,1.));+#799=DIRECTION('ref_axis',(-0.256736417897305,0.966481459586918,0.));+#800=DIRECTION('center_axis',(-0.956855105681015,0.254463875651683,-0.140272744039955));+#801=DIRECTION('ref_axis',(0.182722083236711,0.902314165924266,0.390437941637378));+#802=DIRECTION('',(0.135445313241001,-0.0362924623015003,-0.990119904002107));+#803=DIRECTION('center_axis',(0.,0.,1.));+#804=DIRECTION('ref_axis',(-1.,0.,0.));+#805=DIRECTION('center_axis',(0.,0.,1.));+#806=DIRECTION('ref_axis',(-0.966481459586918,-0.256736417897305,0.));+#807=DIRECTION('center_axis',(-0.254458034360377,-0.956856603863353,-0.140273120705802));+#808=DIRECTION('ref_axis',(0.902384375775184,-0.182761289834245,-0.390257286024175));+#809=DIRECTION('',(0.0362924623015003,0.135445313241001,-0.990119904002107));+#810=DIRECTION('',(-0.135445313241001,-0.0362924623015003,0.990119904002107));+#811=DIRECTION('center_axis',(-0.956855105681172,-0.25446387565106,-0.140272744040013));+#812=DIRECTION('ref_axis',(-0.182722081244468,0.902314162856912,-0.390437949658483));+#813=DIRECTION('center_axis',(0.,1.,0.));+#814=DIRECTION('ref_axis',(1.,0.,0.));+#815=DIRECTION('center_axis',(0.,0.,1.));+#816=DIRECTION('ref_axis',(1.,0.,0.));+#817=DIRECTION('center_axis',(0.,-1.,0.));+#818=DIRECTION('ref_axis',(-1.,0.,0.));+#819=DIRECTION('center_axis',(-0.968100345887708,5.70544447831775E-12,-0.250562807080581));+#820=DIRECTION('ref_axis',(-0.046751035383631,0.982438992669549,0.180632129957545));+#821=DIRECTION('',(0.933012701892336,-0.250000000000174,0.258819045101934));+#822=DIRECTION('',(0.933012701892336,0.250000000000174,0.258819045101934));+#823=DIRECTION('center_axis',(0.258819045102493,0.965925826289076,0.));+#824=DIRECTION('ref_axis',(0.965925826289076,-0.258819045102493,0.));+#825=DIRECTION('center_axis',(0.258819045102493,-0.965925826289076,0.));+#826=DIRECTION('ref_axis',(-0.965925826289076,-0.258819045102493,0.));+#827=DIRECTION('center_axis',(0.,0.,-1.));+#828=DIRECTION('ref_axis',(1.,0.,0.));+#829=DIRECTION('center_axis',(-2.57158511516849E-12,-0.968100345886812,+-0.250562807084044));+#830=DIRECTION('ref_axis',(-0.982438992670233,-0.0467510353863553,0.18063212995312));+#831=DIRECTION('',(0.250000000000431,0.93301270189216,0.258819045102318));+#832=DIRECTION('',(-0.250000000000431,0.93301270189216,0.258819045102318));+#833=DIRECTION('center_axis',(-0.965925826289076,0.258819045102493,0.));+#834=DIRECTION('ref_axis',(0.258819045102493,0.965925826289076,0.));+#835=DIRECTION('center_axis',(0.965925826289076,0.258819045102493,0.));+#836=DIRECTION('ref_axis',(0.258819045102493,-0.965925826289076,0.));+#837=DIRECTION('center_axis',(0.,0.,-1.));+#838=DIRECTION('ref_axis',(1.,0.,0.));+#839=DIRECTION('',(0.,0.,-1.));+#840=DIRECTION('center_axis',(-0.258819045102493,-0.965925826289076,0.));+#841=DIRECTION('ref_axis',(-0.965925826289076,0.258819045102493,0.));+#842=CARTESIAN_POINT('',(0.,0.,0.));+#843=CARTESIAN_POINT('Origin',(0.,0.,0.));+#844=CARTESIAN_POINT('',(-0.435,0.,0.));+#845=CARTESIAN_POINT('Origin',(0.,0.,0.));+#846=CARTESIAN_POINT('',(-0.335,0.,0.));+#847=CARTESIAN_POINT('Origin',(0.,0.,0.));+#848=CARTESIAN_POINT('Origin',(0.,0.,0.0825));+#849=CARTESIAN_POINT('',(0.435000000492065,8.79044664630808E-19,-0.4497766934534));+#850=CARTESIAN_POINT('',(-0.435000000492065,-8.79044664630808E-19,-0.4497766934534));+#851=CARTESIAN_POINT('Origin',(0.,0.,-0.4497766934534));+#852=CARTESIAN_POINT('Origin',(0.,0.,-0.4497766934534));+#853=CARTESIAN_POINT('',(-0.435,5.32721357629099E-17,0.0825));+#854=CARTESIAN_POINT('Ctrl Pts',(-0.421594773337783,-3.49079421202853E-9,+-0.612965998035337));+#855=CARTESIAN_POINT('Ctrl Pts',(-0.42159477358289,-0.42159477819506,-0.612965985033865));+#856=CARTESIAN_POINT('Ctrl Pts',(1.12137642843461E-9,-0.42159477819506,+-0.612965980994025));+#857=CARTESIAN_POINT('Ctrl Pts',(0.421594775825643,-0.42159477819506,-0.612965976954184));+#858=CARTESIAN_POINT('Ctrl Pts',(0.421594775580536,-3.49079397317237E-9,+-0.612965989955655));+#859=CARTESIAN_POINT('Ctrl Pts',(-0.435000000034728,-9.83700059612954E-10,+-0.531921933613031));+#860=CARTESIAN_POINT('Ctrl Pts',(-0.435000000287629,-0.435000001616118,+-0.531921920198158));+#861=CARTESIAN_POINT('Ctrl Pts',(3.44789210291141E-10,-0.435000001616118,+-0.531921916029865));+#862=CARTESIAN_POINT('Ctrl Pts',(0.435000000977207,-0.435000001616118,-0.531921911861572));+#863=CARTESIAN_POINT('Ctrl Pts',(0.435000000724307,-9.83699813162012E-10,+-0.531921925276444));+#864=CARTESIAN_POINT('Ctrl Pts',(-0.435000001180448,1.54955948644732E-9,+-0.4497766934534));+#865=CARTESIAN_POINT('Ctrl Pts',(-0.435000001433348,-0.434999999441439,+-0.449776680038528));+#866=CARTESIAN_POINT('Ctrl Pts',(-4.42349780916981E-10,-0.434999999441439,+-0.449776675870234));+#867=CARTESIAN_POINT('Ctrl Pts',(0.435000000548649,-0.434999999441439,-0.449776671701941));+#868=CARTESIAN_POINT('Ctrl Pts',(0.435000000295749,1.54955973289826E-9,+-0.449776685116813));+#869=CARTESIAN_POINT('',(-0.1100291276079,-0.4106342945452,-0.59));+#870=CARTESIAN_POINT('',(-0.411248496150189,-0.107710755563898,-0.589999999945303));+#871=CARTESIAN_POINT('Origin',(0.,0.,-0.59));+#872=CARTESIAN_POINT('',(-0.1091167579479,-0.4072292846186,-0.612965980387));+#873=CARTESIAN_POINT('Origin',(0.1462327604829,0.5457480918533,-0.4497766934534));+#874=CARTESIAN_POINT('',(0.1091167579479,-0.4072292846186,-0.612965980387));+#875=CARTESIAN_POINT('Ctrl Pts',(-0.109116757947898,-0.4072292846186,-0.612965980387));+#876=CARTESIAN_POINT('Ctrl Pts',(-0.109350281160638,-0.408100805113327,+-0.607511153227363));+#877=CARTESIAN_POINT('Ctrl Pts',(-0.109184347791537,-0.409018416160024,+-0.602137949891247));+#878=CARTESIAN_POINT('Ctrl Pts',(-0.108631392112978,-0.40998574121446,-0.596800830531668));+#879=CARTESIAN_POINT('Ctrl Pts',(-0.108380768287011,-0.410424175460634,+-0.594381813374587));+#880=CARTESIAN_POINT('Ctrl Pts',(-0.108049922796826,-0.410872829296172,+-0.59197018253976));+#881=CARTESIAN_POINT('Ctrl Pts',(-0.107636510704227,-0.411331940263288,+-0.589562083557026));+#882=CARTESIAN_POINT('Ctrl Pts',(-0.106753971966542,-0.412312035477314,+-0.584421351888818));+#883=CARTESIAN_POINT('Ctrl Pts',(-0.105490936909929,-0.413344279962557,+-0.579297017524966));+#884=CARTESIAN_POINT('Ctrl Pts',(-0.103866227590306,-0.41439629921771,-0.574313018839391));+#885=CARTESIAN_POINT('Ctrl Pts',(-0.103050702390725,-0.41492436180876,-0.571811293470012));+#886=CARTESIAN_POINT('Ctrl Pts',(-0.102144872529747,-0.415457436729791,+-0.569344810607068));+#887=CARTESIAN_POINT('Ctrl Pts',(-0.101155873680074,-0.415991786263789,+-0.566927866607994));+#888=CARTESIAN_POINT('Ctrl Pts',(-0.0987466141719913,-0.417293493233266,+-0.561040048525903));+#889=CARTESIAN_POINT('Ctrl Pts',(-0.0958475632952568,-0.418598683082171,+-0.555441722784693));+#890=CARTESIAN_POINT('Ctrl Pts',(-0.0924678464831636,-0.419891862068194,+-0.550122912635699));+#891=CARTESIAN_POINT('Ctrl Pts',(-0.0916962386472333,-0.420187101904652,+-0.548908599183725));+#892=CARTESIAN_POINT('Ctrl Pts',(-0.0908993849104523,-0.420481691250045,+-0.547708949476481));+#893=CARTESIAN_POINT('Ctrl Pts',(-0.0900770736143311,-0.420775394237914,+-0.546524043732508));+#894=CARTESIAN_POINT('Ctrl Pts',(-0.0856529594555881,-0.422355544666623,+-0.540149136439599));+#895=CARTESIAN_POINT('Ctrl Pts',(-0.0804903751371729,-0.423912106474778,+-0.534207058227975));+#896=CARTESIAN_POINT('Ctrl Pts',(-0.0746884788776826,-0.425360077392348,+-0.528866045924748));+#897=CARTESIAN_POINT('Ctrl Pts',(-0.0704700825239476,-0.426412856512451,+-0.524982745393765));+#898=CARTESIAN_POINT('Ctrl Pts',(-0.0659113653994954,-0.427409610569301,+-0.521417451535141));+#899=CARTESIAN_POINT('Ctrl Pts',(-0.0611184059984039,-0.428313525699154,+-0.518249883445926));+#900=CARTESIAN_POINT('Ctrl Pts',(-0.0593192268498737,-0.428652836992569,+-0.517060843026271));+#901=CARTESIAN_POINT('Ctrl Pts',(-0.0574873843519088,-0.428979167538845,+-0.515927455185565));+#902=CARTESIAN_POINT('Ctrl Pts',(-0.0556295628221114,-0.429290962421024,+-0.514852420752448));+#903=CARTESIAN_POINT('Ctrl Pts',(-0.0488225603427207,-0.430433369537108,+-0.510913526765975));+#904=CARTESIAN_POINT('Ctrl Pts',(-0.0416670623062016,-0.431377122489234,+-0.507754487716611));+#905=CARTESIAN_POINT('Ctrl Pts',(-0.0342828714893517,-0.432095320015604,+-0.505375232836491));+#906=CARTESIAN_POINT('Ctrl Pts',(-0.0268986806725018,-0.432813517541973,+-0.50299597795637));+#907=CARTESIAN_POINT('Ctrl Pts',(-0.0192858374828511,-0.433306082430354,+-0.50139965881497));+#908=CARTESIAN_POINT('Ctrl Pts',(-0.0115958284183545,-0.433552583907518,+-0.500600649889925));+#909=CARTESIAN_POINT('Ctrl Pts',(-0.00774360260852997,-0.433676066122496,+-0.500200395120714));+#910=CARTESIAN_POINT('Ctrl Pts',(-0.00382494856693413,-0.433737876172953,+-0.500000000000387));+#911=CARTESIAN_POINT('Ctrl Pts',(-3.19698976399976E-17,-0.433737876322974,+-0.500000000000395));+#912=CARTESIAN_POINT('Ctrl Pts',(0.00381060827812319,-0.433737876472433,+-0.500000000000403));+#913=CARTESIAN_POINT('Ctrl Pts',(0.00771399441341037,-0.433676538948857,+-0.500198858118451));+#914=CARTESIAN_POINT('Ctrl Pts',(0.0115518355573819,-0.433553990550268,+-0.500596087708366));+#915=CARTESIAN_POINT('Ctrl Pts',(0.0192419606183529,-0.433308432572275,+-0.501392041764246));+#916=CARTESIAN_POINT('Ctrl Pts',(0.0268550301203556,-0.432816828783024,+-0.502985194439097));+#917=CARTESIAN_POINT('Ctrl Pts',(0.0342405079836257,-0.432099440946605,+-0.505361592263085));+#918=CARTESIAN_POINT('Ctrl Pts',(0.0416259858468959,-0.431382053110187,+-0.507737990087072));+#919=CARTESIAN_POINT('Ctrl Pts',(0.0487837900960855,-0.430438959935386,+-0.510894601847888));+#920=CARTESIAN_POINT('Ctrl Pts',(0.0555932425533862,-0.429297048591832,+-0.514831413522586));+#921=CARTESIAN_POINT('Ctrl Pts',(0.0605354096475284,-0.428468271725861,+-0.517688674254859));+#922=CARTESIAN_POINT('Ctrl Pts',(0.0652919793650865,-0.427535836413629,+-0.52096181715331));+#923=CARTESIAN_POINT('Ctrl Pts',(0.0697452688743458,-0.426532544726783,+-0.524586294857913));+#924=CARTESIAN_POINT('Ctrl Pts',(0.071427842967876,-0.426153473795727,-0.525955721273076));+#925=CARTESIAN_POINT('Ctrl Pts',(0.0730674172440476,-0.425764399823899,+-0.527374869395907));+#926=CARTESIAN_POINT('Ctrl Pts',(0.0746588772994185,-0.425367467882685,+-0.52883880560118));+#927=CARTESIAN_POINT('Ctrl Pts',(0.0804624742207109,-0.423919971299775,+-0.534177359732785));+#928=CARTESIAN_POINT('Ctrl Pts',(0.0856261181211008,-0.422363988342928,+-0.540116110772606));+#929=CARTESIAN_POINT('Ctrl Pts',(0.0900519887041404,-0.420784354358351,+-0.546487913015413));+#930=CARTESIAN_POINT('Ctrl Pts',(0.0935695952415433,-0.419528888179077,+-0.551552112883847));+#931=CARTESIAN_POINT('Ctrl Pts',(0.096625369162184,-0.418257662028362,-0.556883650829066));+#932=CARTESIAN_POINT('Ctrl Pts',(0.0992229397767676,-0.416985080524721,+-0.562493072460409));+#933=CARTESIAN_POINT('Ctrl Pts',(0.0998936458293468,-0.416656493454903,+-0.563941453889767));+#934=CARTESIAN_POINT('Ctrl Pts',(0.100533567593754,-0.416327782921751,-0.565408489690081));+#935=CARTESIAN_POINT('Ctrl Pts',(0.101142244781152,-0.415999140758955,-0.566894578286705));+#936=CARTESIAN_POINT('Ctrl Pts',(0.103069308133959,-0.414958664327076,-0.571599513731746));+#937=CARTESIAN_POINT('Ctrl Pts',(0.104680216950115,-0.413921730415963,-0.576498617113661));+#938=CARTESIAN_POINT('Ctrl Pts',(0.105929985843235,-0.412918390359016,-0.581475182203665));+#939=CARTESIAN_POINT('Ctrl Pts',(0.106603784010251,-0.412377451394231,-0.584158238610445));+#940=CARTESIAN_POINT('Ctrl Pts',(0.107173451954694,-0.411846364678513,-0.586863472649683));+#941=CARTESIAN_POINT('Ctrl Pts',(0.107638075340547,-0.411330200190734,-0.589571201903103));+#942=CARTESIAN_POINT('Ctrl Pts',(0.108541739752711,-0.410326291449521,-0.594837571695492));+#943=CARTESIAN_POINT('Ctrl Pts',(0.109052712177428,-0.409373748779828,-0.600113147393514));+#944=CARTESIAN_POINT('Ctrl Pts',(0.109184812582585,-0.408468742842044,-0.605443630040472));+#945=CARTESIAN_POINT('Ctrl Pts',(0.109246611165165,-0.408045367273171,-0.607937311052717));+#946=CARTESIAN_POINT('Ctrl Pts',(0.109224761250832,-0.407632358434291,-0.610443150447865));+#947=CARTESIAN_POINT('Ctrl Pts',(0.109116757947463,-0.407229284618717,-0.612965980387));+#948=CARTESIAN_POINT('',(0.1100291276079,-0.4106342945452,-0.59));+#949=CARTESIAN_POINT('Origin',(-0.1462327604829,0.5457480918533,-0.4497766934534));+#950=CARTESIAN_POINT('',(0.4112484965242,-0.1077107556622,-0.59));+#951=CARTESIAN_POINT('Origin',(0.,0.,-0.59));+#952=CARTESIAN_POINT('',(0.4337380134347,0.,-0.5));+#953=CARTESIAN_POINT('Ctrl Pts',(0.41124849652408,-0.107710755662755,-0.589999999999884));+#954=CARTESIAN_POINT('Ctrl Pts',(0.412335365106771,-0.106754178724151,-0.584287343180281));+#955=CARTESIAN_POINT('Ctrl Pts',(0.413450266924349,-0.10536706227487,-0.578768617992668));+#956=CARTESIAN_POINT('Ctrl Pts',(0.414588514085099,-0.103566593935282,-0.573407235034667));+#957=CARTESIAN_POINT('Ctrl Pts',(0.414919555429471,-0.103042955941542,-0.571847960842464));+#958=CARTESIAN_POINT('Ctrl Pts',(0.415252558744439,-0.102484019572242,-0.570302044395535));+#959=CARTESIAN_POINT('Ctrl Pts',(0.41558735785394,-0.10188941154278,-0.568768782073256));+#960=CARTESIAN_POINT('Ctrl Pts',(0.416868551661492,-0.0996139929301025,+-0.562901364458009));+#961=CARTESIAN_POINT('Ctrl Pts',(0.418179100460788,-0.0968126169870629,+-0.557221600045027));+#962=CARTESIAN_POINT('Ctrl Pts',(0.419472455342409,-0.0935444334403834,+-0.551853719374602));+#963=CARTESIAN_POINT('Ctrl Pts',(0.41967917214693,-0.0930220799324948,-0.550995771447849));+#964=CARTESIAN_POINT('Ctrl Pts',(0.419885461009904,-0.0924879001160726,+-0.550145747167932));+#965=CARTESIAN_POINT('Ctrl Pts',(0.420091146313087,-0.0919422448308152,+-0.54930409797528));+#966=CARTESIAN_POINT('Ctrl Pts',(0.421583732754105,-0.0879826149124715,+-0.543196543601639));+#967=CARTESIAN_POINT('Ctrl Pts',(0.423042803560736,-0.0834187976160491,+-0.537528124715941));+#968=CARTESIAN_POINT('Ctrl Pts',(0.424419963758391,-0.0783103373180915,+-0.532361072540811));+#969=CARTESIAN_POINT('Ctrl Pts',(0.425647567685724,-0.0737566434667363,+-0.52775514974859));+#970=CARTESIAN_POINT('Ctrl Pts',(0.426810703038626,-0.0687666605278043,+-0.523551336661771));+#971=CARTESIAN_POINT('Ctrl Pts',(0.427862466337567,-0.0634558929164214,+-0.519838841448331));+#972=CARTESIAN_POINT('Ctrl Pts',(0.427990600333191,-0.0628088938486997,+-0.519386556370582));+#973=CARTESIAN_POINT('Ctrl Pts',(0.428117093408089,-0.0621571752992446,+-0.518941516824304));+#974=CARTESIAN_POINT('Ctrl Pts',(0.428241871215412,-0.0615009786672012,+-0.518503843688651));+#975=CARTESIAN_POINT('Ctrl Pts',(0.429390863694461,-0.0554585179831486,+-0.514473614660472));+#976=CARTESIAN_POINT('Ctrl Pts',(0.430392859164001,-0.0490356625376673,+-0.511067306867878));+#977=CARTESIAN_POINT('Ctrl Pts',(0.431211799817641,-0.0423348025940657,+-0.508318555234264));+#978=CARTESIAN_POINT('Ctrl Pts',(0.432030740471281,-0.0356339426504594,+-0.505569803600648));+#979=CARTESIAN_POINT('Ctrl Pts',(0.432666700118517,-0.0286530521637485,+-0.503483081870347));+#980=CARTESIAN_POINT('Ctrl Pts',(0.433093524597873,-0.021573329802306,-0.502094248777366));+#981=CARTESIAN_POINT('Ctrl Pts',(0.433437698864698,-0.0158645235577617,+-0.500974349065335));+#982=CARTESIAN_POINT('Ctrl Pts',(0.433645286200676,-0.0100929345254701,+-0.500301088787472));+#983=CARTESIAN_POINT('Ctrl Pts',(0.43371340117873,-0.00423523222685219,+-0.500079922610463));+#984=CARTESIAN_POINT('Ctrl Pts',(0.433729758349953,-0.00282856012323822,+-0.500026811633564));+#985=CARTESIAN_POINT('Ctrl Pts',(0.4337380134347,-0.001416914414037,-0.499999999999675));+#986=CARTESIAN_POINT('Ctrl Pts',(0.4337380134347,-2.52418620635553E-17,+-0.49999999999968));+#987=CARTESIAN_POINT('Origin',(-0.565,0.,-0.4497766934534));+#988=CARTESIAN_POINT('',(-0.4337380134347,0.,-0.5));+#989=CARTESIAN_POINT('Origin',(0.565,0.,-0.4497766934534));+#990=CARTESIAN_POINT('Ctrl Pts',(-0.4337380134347,1.02443375240448E-17,+-0.50000000000086));+#991=CARTESIAN_POINT('Ctrl Pts',(-0.4337380134347,-0.00590035360668503,+-0.500000000000888));+#992=CARTESIAN_POINT('Ctrl Pts',(-0.433595900266174,-0.0117089937891322,+-0.500460930105732));+#993=CARTESIAN_POINT('Ctrl Pts',(-0.433317697622654,-0.017449358404652,+-0.501365210318225));+#994=CARTESIAN_POINT('Ctrl Pts',(-0.433250889874865,-0.0188278529674483,+-0.501582364704689));+#995=CARTESIAN_POINT('Ctrl Pts',(-0.433176174810409,-0.0202024136851877,+-0.501825315397726));+#996=CARTESIAN_POINT('Ctrl Pts',(-0.43309352459788,-0.021573329802049,-0.502094248778678));+#997=CARTESIAN_POINT('Ctrl Pts',(-0.432666700118528,-0.028653052163398,+-0.503483081871689));+#998=CARTESIAN_POINT('Ctrl Pts',(-0.432030740471299,-0.0356339426500078,+-0.505569803602007));+#999=CARTESIAN_POINT('Ctrl Pts',(-0.431211799817669,-0.0423348025935119,+-0.508318555235622));+#1000=CARTESIAN_POINT('Ctrl Pts',(-0.430392859164039,-0.0490356625370114,+-0.511067306869236));+#1001=CARTESIAN_POINT('Ctrl Pts',(-0.429390863694512,-0.0554585179823892,+-0.514473614661813));+#1002=CARTESIAN_POINT('Ctrl Pts',(-0.428241871215479,-0.0615009786663426,+-0.518503843689957));+#1003=CARTESIAN_POINT('Ctrl Pts',(-0.427217656543755,-0.0668872427183308,+-0.522096399582496));+#1004=CARTESIAN_POINT('Ctrl Pts',(-0.426077030658468,-0.0719691296520287,+-0.526188533430569));+#1005=CARTESIAN_POINT('Ctrl Pts',(-0.424865704034675,-0.0766269248033127,+-0.530695646481541));+#1006=CARTESIAN_POINT('Ctrl Pts',(-0.424718130792243,-0.0771943736808975,+-0.531244738092239));+#1007=CARTESIAN_POINT('Ctrl Pts',(-0.42456952002882,-0.0777555708704115,+-0.531799943159116));+#1008=CARTESIAN_POINT('Ctrl Pts',(-0.424419963758513,-0.0783103373169548,+-0.532361072541968));+#1009=CARTESIAN_POINT('Ctrl Pts',(-0.423042803560878,-0.0834187976148281,+-0.537528124717033));+#1010=CARTESIAN_POINT('Ctrl Pts',(-0.42158373275427,-0.087982614911177,+-0.543196543602652));+#1011=CARTESIAN_POINT('Ctrl Pts',(-0.420091146313274,-0.0919422448294608,+-0.549304097976204));+#1012=CARTESIAN_POINT('Ctrl Pts',(-0.418804245175444,-0.0953562194625333,+-0.554570003157175));+#1013=CARTESIAN_POINT('Ctrl Pts',(-0.417493220642234,-0.0983172277711325,+-0.560165675685106));+#1014=CARTESIAN_POINT('Ctrl Pts',(-0.416203758307136,-0.100758524562346,+-0.565970220616314));+#1015=CARTESIAN_POINT('Ctrl Pts',(-0.41599766364802,-0.101148716827982,+-0.566897960555942));+#1016=CARTESIAN_POINT('Ctrl Pts',(-0.415792130955236,-0.101525731571307,+-0.567830993198341));+#1017=CARTESIAN_POINT('Ctrl Pts',(-0.415587357854201,-0.10188941154129,+-0.56876878207389));+#1018=CARTESIAN_POINT('Ctrl Pts',(-0.414436190055115,-0.103933902094499,+-0.574040726242413));+#1019=CARTESIAN_POINT('Ctrl Pts',(-0.413306528691451,-0.105560445530728,+-0.579461208407975));+#1020=CARTESIAN_POINT('Ctrl Pts',(-0.412203775352324,-0.106766751582098,+-0.585064060457944));+#1021=CARTESIAN_POINT('Ctrl Pts',(-0.411883056829601,-0.10711758678796,+-0.586693562150431));+#1022=CARTESIAN_POINT('Ctrl Pts',(-0.411564595214719,-0.107432550253235,+-0.588338563311708));+#1023=CARTESIAN_POINT('Ctrl Pts',(-0.411248496524411,-0.107710755661222,+-0.590000000000204));+#1024=CARTESIAN_POINT('Origin',(0.,0.,-1.12));+#1025=CARTESIAN_POINT('',(0.07117523740319,-0.2656296022295,-1.65));+#1026=CARTESIAN_POINT('',(0.1100291276079,-0.4106342945452,-0.59));+#1027=CARTESIAN_POINT('',(0.2656296022295,-0.07117523740319,-1.65));+#1028=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1029=CARTESIAN_POINT('',(0.4074829924761,-0.1091847387634,-0.613036359306));+#1030=CARTESIAN_POINT('',(0.4074829924761,-0.1091847387634,-0.613036359306));+#1031=CARTESIAN_POINT('Origin',(0.438078258287466,0.00940988922961147,-0.616847431334544));+#1032=CARTESIAN_POINT('Origin',(0.,0.,-1.12));+#1033=CARTESIAN_POINT('',(0.4074829924761,0.1091847387634,-0.613036359306));+#1034=CARTESIAN_POINT('',(0.2656296022295,0.07117523740319,-1.65));+#1035=CARTESIAN_POINT('',(0.4074829924761,0.1091847387634,-0.613036359306));+#1036=CARTESIAN_POINT('',(0.07117523740319,0.2656296022295,-1.65));+#1037=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1038=CARTESIAN_POINT('',(0.1100291276079,0.4106342945452,-0.59));+#1039=CARTESIAN_POINT('',(0.1100291276079,0.4106342945452,-0.59));+#1040=CARTESIAN_POINT('',(0.411248496150125,0.107710755564194,-0.589999999945241));+#1041=CARTESIAN_POINT('Origin',(0.,0.,-0.59));+#1042=CARTESIAN_POINT('Origin',(0.438078258286677,-0.00940988922638442,+-0.616847431333858));+#1043=CARTESIAN_POINT('Origin',(0.,0.,-1.12));+#1044=CARTESIAN_POINT('',(-0.4112484965242,0.1077107556622,-0.59));+#1045=CARTESIAN_POINT('',(-0.1100291276079,0.4106342945452,-0.59));+#1046=CARTESIAN_POINT('Origin',(0.,0.,-0.59));+#1047=CARTESIAN_POINT('',(-0.07117523740319,0.2656296022295,-1.65));+#1048=CARTESIAN_POINT('',(-0.1100291276079,0.4106342945452,-0.59));+#1049=CARTESIAN_POINT('',(-0.2656296022295,0.07117523740319,-1.65));+#1050=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1051=CARTESIAN_POINT('',(-0.4074829924761,0.1091847387634,-0.613036359306));+#1052=CARTESIAN_POINT('',(-0.4074829924761,0.1091847387634,-0.613036359306));+#1053=CARTESIAN_POINT('Origin',(-0.438076151933698,-0.00940803503759271,+-0.616847026987834));+#1054=CARTESIAN_POINT('Origin',(0.,0.,-1.12));+#1055=CARTESIAN_POINT('',(-0.4074829924761,-0.1091847387634,-0.613036359306));+#1056=CARTESIAN_POINT('',(-0.2656296022295,-0.07117523740319,-1.65));+#1057=CARTESIAN_POINT('',(-0.4074829924761,-0.1091847387634,-0.613036359306));+#1058=CARTESIAN_POINT('',(-0.07117523740319,-0.2656296022295,-1.65));+#1059=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1060=CARTESIAN_POINT('',(-0.1100291276079,-0.4106342945452,-0.59));+#1061=CARTESIAN_POINT('Origin',(-0.438076151931735,0.00940803502977114,+-0.616847026987093));+#1062=CARTESIAN_POINT('Origin',(0.319771950981689,-8.30759072645293E-14,+-0.582763071003387));+#1063=CARTESIAN_POINT('',(0.3134932330786,0.08400025863603,-0.5869634669142));+#1064=CARTESIAN_POINT('',(0.3134932330786,0.08400025863603,-0.5869634669142));+#1065=CARTESIAN_POINT('Ctrl Pts',(0.4337380134347,-2.52418620635553E-17,+-0.49999999999968));+#1066=CARTESIAN_POINT('Ctrl Pts',(0.4337380134347,0.00590035360673006,-0.499999999999698));+#1067=CARTESIAN_POINT('Ctrl Pts',(0.433595900266173,0.0117089937892233,+-0.500460930104539));+#1068=CARTESIAN_POINT('Ctrl Pts',(0.433317697622653,0.0174493584047889,+-0.501365210317032));+#1069=CARTESIAN_POINT('Ctrl Pts',(0.433250889874863,0.0188278529676167,+-0.501582364703499));+#1070=CARTESIAN_POINT('Ctrl Pts',(0.433176174810405,0.0202024136853875,+-0.50182531539654));+#1071=CARTESIAN_POINT('Ctrl Pts',(0.433093524597874,0.0215733298022802,+-0.502094248777498));+#1072=CARTESIAN_POINT('Ctrl Pts',(0.432666700118519,0.0286530521637058,+-0.50348308187052));+#1073=CARTESIAN_POINT('Ctrl Pts',(0.432030740471284,0.0356339426503914,+-0.50556980360086));+#1074=CARTESIAN_POINT('Ctrl Pts',(0.431211799817646,0.0423348025939663,+-0.508318555234508));+#1075=CARTESIAN_POINT('Ctrl Pts',(0.430392859164008,0.0490356625375365,+-0.511067306868154));+#1076=CARTESIAN_POINT('Ctrl Pts',(0.429390863694472,0.0554585179829801,+-0.514473614660774));+#1077=CARTESIAN_POINT('Ctrl Pts',(0.428241871215428,0.0615009786669916,+-0.518503843688969));+#1078=CARTESIAN_POINT('Ctrl Pts',(0.427217656543703,0.0668872427189912,+-0.522096399581527));+#1079=CARTESIAN_POINT('Ctrl Pts',(0.426077030658415,0.0719691296526989,+-0.526188533429619));+#1080=CARTESIAN_POINT('Ctrl Pts',(0.424865704034621,0.0766269248039919,+-0.530695646480608));+#1081=CARTESIAN_POINT('Ctrl Pts',(0.424718130792179,0.0771943736816169,+-0.531244738091346));+#1082=CARTESIAN_POINT('Ctrl Pts',(0.424569520028745,0.0777555708711703,+-0.531799943158264));+#1083=CARTESIAN_POINT('Ctrl Pts',(0.424419963758428,0.078310337317752,-0.532361072541157));+#1084=CARTESIAN_POINT('Ctrl Pts',(0.42304280356078,0.0834187976156653,-0.537528124716287));+#1085=CARTESIAN_POINT('Ctrl Pts',(0.421583732754159,0.0879826149120438,+-0.543196543601976));+#1086=CARTESIAN_POINT('Ctrl Pts',(0.420091146313151,0.0919422448303471,+-0.549304097975599));+#1087=CARTESIAN_POINT('Ctrl Pts',(0.418804245175317,0.0953562194634183,+-0.554570003156604));+#1088=CARTESIAN_POINT('Ctrl Pts',(0.417493220642103,0.0983172277720134,+-0.560165675684566));+#1089=CARTESIAN_POINT('Ctrl Pts',(0.416203758307001,0.100758524563222,-0.565970220615803));+#1090=CARTESIAN_POINT('Ctrl Pts',(0.415997663647878,0.101148716828872,-0.56689796055547));+#1091=CARTESIAN_POINT('Ctrl Pts',(0.415792130955085,0.101525731572209,-0.567830993197911));+#1092=CARTESIAN_POINT('Ctrl Pts',(0.415587357854041,0.101889411542205,-0.568768782073501));+#1093=CARTESIAN_POINT('Ctrl Pts',(0.414436190054948,0.10393390209541,-0.57404072624207));+#1094=CARTESIAN_POINT('Ctrl Pts',(0.413306528691278,0.105560445531632,-0.579461208407676));+#1095=CARTESIAN_POINT('Ctrl Pts',(0.412203775352145,0.106766751582991,-0.585064060457687));+#1096=CARTESIAN_POINT('Ctrl Pts',(0.411883056829417,0.107117586788852,-0.586693562150198));+#1097=CARTESIAN_POINT('Ctrl Pts',(0.411564595214531,0.107432550254126,-0.588338563311499));+#1098=CARTESIAN_POINT('Ctrl Pts',(0.411248496524219,0.107710755662111,-0.590000000000019));+#1099=CARTESIAN_POINT('',(0.3134932330786,-0.08400025863603,-0.5869634669142));+#1100=CARTESIAN_POINT('',(0.3134932330786,-0.08400025863603,-0.5869634669142));+#1101=CARTESIAN_POINT('',(0.3151772523208,-0.08372377758611,-0.5759776693453));+#1102=CARTESIAN_POINT('Origin',(0.337261589048336,0.00700086452435883,-0.589912773297936));+#1103=CARTESIAN_POINT('',(0.3335975848396,0.,-0.5));+#1104=CARTESIAN_POINT('Ctrl Pts',(0.3335975848396,-3.44294531470913E-18,+-0.499999999999708));+#1105=CARTESIAN_POINT('Ctrl Pts',(0.3335975848396,-0.00489738672783748,+-0.499999999999701));+#1106=CARTESIAN_POINT('Ctrl Pts',(0.333467936944575,-0.0097099577936956,+-0.500412423450436));+#1107=CARTESIAN_POINT('Ctrl Pts',(0.333215435045597,-0.0144573198281293,+-0.501219830155054));+#1108=CARTESIAN_POINT('Ctrl Pts',(0.333161212239585,-0.015476778658745,+-0.501393214423823));+#1109=CARTESIAN_POINT('Ctrl Pts',(0.333101276089344,-0.0164932328009472,+-0.501584998603484));+#1110=CARTESIAN_POINT('Ctrl Pts',(0.333035616046399,-0.0175068508243247,+-0.501795310686569));+#1111=CARTESIAN_POINT('Ctrl Pts',(0.332664193783638,-0.0232406317195058,+-0.502984992969448));+#1112=CARTESIAN_POINT('Ctrl Pts',(0.332111946297925,-0.0288818654186155,+-0.504770955125285));+#1113=CARTESIAN_POINT('Ctrl Pts',(0.331402441846759,-0.0342819607037802,+-0.507127204102421));+#1114=CARTESIAN_POINT('Ctrl Pts',(0.330692937395595,-0.0396820559889411,+-0.509483453079556));+#1115=CARTESIAN_POINT('Ctrl Pts',(0.329826121305491,-0.044841778642995,+-0.512409780913075));+#1116=CARTESIAN_POINT('Ctrl Pts',(0.328846826474069,-0.0496337800404193,+-0.515849012851062));+#1117=CARTESIAN_POINT('Ctrl Pts',(0.327867531642647,-0.0544257814378469,+-0.519288244789051));+#1118=CARTESIAN_POINT('Ctrl Pts',(0.326775720034315,-0.0588504153113541,+-0.523239836764691));+#1119=CARTESIAN_POINT('Ctrl Pts',(0.325624845085141,-0.0628064378067831,+-0.5276276829584));+#1120=CARTESIAN_POINT('Ctrl Pts',(0.324473970135969,-0.0667624603022093,+-0.532015529152106));+#1121=CARTESIAN_POINT('Ctrl Pts',(0.323264127199839,-0.0702497651847079,+-0.536838975202455));+#1122=CARTESIAN_POINT('Ctrl Pts',(0.322049860451666,-0.073188602986447,+-0.542008881797373));+#1123=CARTESIAN_POINT('Ctrl Pts',(0.321005863853219,-0.0757153432377297,+-0.546453839964516));+#1124=CARTESIAN_POINT('Ctrl Pts',(0.319959074809375,-0.0778347857194511,+-0.551157265417267));+#1125=CARTESIAN_POINT('Ctrl Pts',(0.318951187506329,-0.0794975048588054,+-0.556001802443122));+#1126=CARTESIAN_POINT('Ctrl Pts',(0.31878680658569,-0.0797686852795394,+-0.55679192000287));+#1127=CARTESIAN_POINT('Ctrl Pts',(0.318623469562294,-0.0800278236680716,+-0.55758575587909));+#1128=CARTESIAN_POINT('Ctrl Pts',(0.318461366050266,-0.080274832424007,+-0.558382767208169));+#1129=CARTESIAN_POINT('Ctrl Pts',(0.317558298056109,-0.0816509019426226,+-0.562822864854847));+#1130=CARTESIAN_POINT('Ctrl Pts',(0.316690987084049,-0.0826540312778184,+-0.567359782952275));+#1131=CARTESIAN_POINT('Ctrl Pts',(0.315863361522207,-0.0832839518711512,+-0.572025584906971));+#1132=CARTESIAN_POINT('Ctrl Pts',(0.315631534673147,-0.083460399425508,+-0.573332526373455));+#1133=CARTESIAN_POINT('Ctrl Pts',(0.315402807565895,-0.0836072561334219,+-0.574649634644172));+#1134=CARTESIAN_POINT('Ctrl Pts',(0.315177252320755,-0.0837237775862994,+-0.575977669345276));+#1135=CARTESIAN_POINT('',(0.3151772523208,0.08372377758611,-0.5759776693453));+#1136=CARTESIAN_POINT('Ctrl Pts',(0.315177252320807,0.0837237775860786,+-0.575977669345304));+#1137=CARTESIAN_POINT('Ctrl Pts',(0.315982932360349,0.083307564659159,-0.571233948799861));+#1138=CARTESIAN_POINT('Ctrl Pts',(0.316829077480533,0.0825083940599987,+-0.566629764112074));+#1139=CARTESIAN_POINT('Ctrl Pts',(0.317712220903149,0.0813412205777832,+-0.562131121131336));+#1140=CARTESIAN_POINT('Ctrl Pts',(0.317959367998803,0.0810145878600761,+-0.56087217890451));+#1141=CARTESIAN_POINT('Ctrl Pts',(0.318209404492153,0.0806588209017279,+-0.559621533727573));+#1142=CARTESIAN_POINT('Ctrl Pts',(0.318462217244647,0.0802735359630155,+-0.558378584134841));+#1143=CARTESIAN_POINT('Ctrl Pts',(0.319457549630515,0.0787566560845102,+-0.553485049408276));+#1144=CARTESIAN_POINT('Ctrl Pts',(0.320498977465152,0.0767785891006918,+-0.548712878370071));+#1145=CARTESIAN_POINT('Ctrl Pts',(0.321544013587405,0.0743809761375207,+-0.544182150640913));+#1146=CARTESIAN_POINT('Ctrl Pts',(0.321712916054747,0.0739934653744121,+-0.543449878257199));+#1147=CARTESIAN_POINT('Ctrl Pts',(0.321881921888001,0.0735950998208122,+-0.542723877701843));+#1148=CARTESIAN_POINT('Ctrl Pts',(0.322050864047508,0.0731861735039565,+-0.542004608270293));+#1149=CARTESIAN_POINT('Ctrl Pts',(0.323265087913289,0.0702471315577714,+-0.536835063188948));+#1150=CARTESIAN_POINT('Ctrl Pts',(0.32447482593786,0.0667598105994081,-0.532012175889439));+#1151=CARTESIAN_POINT('Ctrl Pts',(0.325625596190337,0.0628038562340689,+-0.52762481971362));+#1152=CARTESIAN_POINT('Ctrl Pts',(0.326776366442816,0.0588479018687269,+-0.523237463537798));+#1153=CARTESIAN_POINT('Ctrl Pts',(0.327868075100701,0.0544234085412131,+-0.519286302474599));+#1154=CARTESIAN_POINT('Ctrl Pts',(0.328847281066458,0.0496315555388093,+-0.515847416375792));+#1155=CARTESIAN_POINT('Ctrl Pts',(0.329826487032215,0.0448397025364088,+-0.512408530276987));+#1156=CARTESIAN_POINT('Ctrl Pts',(0.330693228950655,0.0396801295745825,+-0.509482470727628));+#1157=CARTESIAN_POINT('Ctrl Pts',(0.331402666665895,0.0342802504195448,+-0.507126457865922));+#1158=CARTESIAN_POINT('Ctrl Pts',(0.332112104381136,0.0288803712645033,+-0.504770445004214));+#1159=CARTESIAN_POINT('Ctrl Pts',(0.332664292384814,0.0232394241569825,+-0.502984677336385));+#1160=CARTESIAN_POINT('Ctrl Pts',(0.333035675132515,0.0175059361048368,+-0.501795120900751));+#1161=CARTESIAN_POINT('Ctrl Pts',(0.333341428972247,0.0127856414819754,+-0.500815776948929));+#1162=CARTESIAN_POINT('Ctrl Pts',(0.333524079573343,0.00800425093696496,+-0.50023426894306));+#1163=CARTESIAN_POINT('Ctrl Pts',(0.333579637568278,0.00314219531370886,+-0.500057197208565));+#1164=CARTESIAN_POINT('Ctrl Pts',(0.333591562882249,0.00209857342061351,+-0.500019189428916));+#1165=CARTESIAN_POINT('Ctrl Pts',(0.3335975848396,0.00105123145345343,-0.499999999999709));+#1166=CARTESIAN_POINT('Ctrl Pts',(0.3335975848396,-3.44294531470913E-18,+-0.499999999999708));+#1167=CARTESIAN_POINT('Origin',(0.337261589086643,-0.00700086467620174,+-0.589912773311632));+#1168=CARTESIAN_POINT('Origin',(0.2656296022295,0.07117523740319,-1.65));+#1169=CARTESIAN_POINT('',(0.1680731525122,0.04503506548499,-1.65));+#1170=CARTESIAN_POINT('',(0.1680731525122,0.04503506548499,-1.65));+#1171=CARTESIAN_POINT('',(0.1680731525122,0.04503506548499,-1.65));+#1172=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1173=CARTESIAN_POINT('',(-0.04503506548499,0.1680731525122,-1.65));+#1174=CARTESIAN_POINT('',(-0.04503506548499,0.1680731525122,-1.65));+#1175=CARTESIAN_POINT('',(-0.1680731525122,0.04503506548499,-1.65));+#1176=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1177=CARTESIAN_POINT('',(-0.2656296022295,0.07117523740319,-1.65));+#1178=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1179=CARTESIAN_POINT('',(0.04503506548499,0.1680731525122,-1.65));+#1180=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1181=CARTESIAN_POINT('',(0.07117523740319,0.2656296022295,-1.65));+#1182=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1183=CARTESIAN_POINT('',(-0.04503506548499,-0.1680731525122,-1.65));+#1184=CARTESIAN_POINT('',(-0.07117523740319,-0.2656296022295,-1.65));+#1185=CARTESIAN_POINT('',(-0.1680731525122,-0.04503506548499,-1.65));+#1186=CARTESIAN_POINT('',(-0.1680731525122,-0.04503506548499,-1.65));+#1187=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1188=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1189=CARTESIAN_POINT('',(0.04503506548499,-0.1680731525122,-1.65));+#1190=CARTESIAN_POINT('',(0.04503506548499,-0.1680731525122,-1.65));+#1191=CARTESIAN_POINT('',(0.1680731525122,-0.04503506548499,-1.65));+#1192=CARTESIAN_POINT('Origin',(0.,0.,-1.65));+#1193=CARTESIAN_POINT('',(0.2656296022295,-0.07117523740319,-1.65));+#1194=CARTESIAN_POINT('Origin',(-0.07117523740319,0.2656296022295,-1.65));+#1195=CARTESIAN_POINT('',(-0.1091167579479,0.4072292846186,-0.612965980387));+#1196=CARTESIAN_POINT('',(-0.08400025863603,0.3134932330786,-0.5869634669142));+#1197=CARTESIAN_POINT('',(-0.1091167579479,0.4072292846186,-0.612965980387));+#1198=CARTESIAN_POINT('',(-0.04503506548499,0.1680731525122,-1.65));+#1199=CARTESIAN_POINT('Origin',(0.1462327604829,-0.5457480918533,-0.4497766934534));+#1200=CARTESIAN_POINT('Origin',(5.13911829758129E-13,0.401200496554915,+-0.60383832941405));+#1201=CARTESIAN_POINT('',(0.1091167579479,0.4072292846186,-0.612965980387));+#1202=CARTESIAN_POINT('',(0.08400025863603,0.3134932330786,-0.5869634669142));+#1203=CARTESIAN_POINT('',(0.1091167579479,0.4072292846186,-0.612965980387));+#1204=CARTESIAN_POINT('',(0.08372377758611,0.3151772523208,-0.5759776693453));+#1205=CARTESIAN_POINT('Origin',(-0.00700756595913425,0.337262594371428,+-0.589913312693652));+#1206=CARTESIAN_POINT('',(-0.08372377758611,0.3151772523208,-0.5759776693453));+#1207=CARTESIAN_POINT('Ctrl Pts',(-0.0837237775863238,0.31517725232075,+-0.575977669345273));+#1208=CARTESIAN_POINT('Ctrl Pts',(-0.0833113327840684,0.315975638250223,+-0.571276895401599));+#1209=CARTESIAN_POINT('Ctrl Pts',(-0.0825230902400074,0.316813484530678,+-0.566714996787816));+#1210=CARTESIAN_POINT('Ctrl Pts',(-0.0813735303111107,0.317687797688976,+-0.562255990679941));+#1211=CARTESIAN_POINT('Ctrl Pts',(-0.0809980749845387,0.31797335525246,+-0.560799644166519));+#1212=CARTESIAN_POINT('Ctrl Pts',(-0.0805836611437083,0.318262792863844,+-0.559354312006106));+#1213=CARTESIAN_POINT('Ctrl Pts',(-0.080129621328708,0.318555943250618,+-0.557918983885863));+#1214=CARTESIAN_POINT('Ctrl Pts',(-0.0786472399050521,0.319513041446269,+-0.553232822734341));+#1215=CARTESIAN_POINT('Ctrl Pts',(-0.0767386134609911,0.320513103970944,+-0.548654970226719));+#1216=CARTESIAN_POINT('Ctrl Pts',(-0.0744441546424222,0.321516271080799,+-0.544301820885805));+#1217=CARTESIAN_POINT('Ctrl Pts',(-0.0738841127241031,0.321761128660308,+-0.543239284444582));+#1218=CARTESIAN_POINT('Ctrl Pts',(-0.0733013145463806,0.322006191464224,+-0.542190056917317));+#1219=CARTESIAN_POINT('Ctrl Pts',(-0.072696780442275,0.322250927401882,+-0.541155647199318));+#1220=CARTESIAN_POINT('Ctrl Pts',(-0.0696155056498017,0.323498332089885,+-0.535883321672081));+#1221=CARTESIAN_POINT('Ctrl Pts',(-0.0659712429459372,0.324734823618324,+-0.530992338852893));+#1222=CARTESIAN_POINT('Ctrl Pts',(-0.0617978793291544,0.325914138928771,+-0.526529257994722));+#1223=CARTESIAN_POINT('Ctrl Pts',(-0.0583079344381202,0.326900332757189,+-0.522797039173288));+#1224=CARTESIAN_POINT('Ctrl Pts',(-0.0544438342087744,0.327847371036208,+-0.519369031417129));+#1225=CARTESIAN_POINT('Ctrl Pts',(-0.0503013782069154,0.328709149482636,+-0.516333191747931));+#1226=CARTESIAN_POINT('Ctrl Pts',(-0.0494901814428825,0.328877907312952,+-0.515738698212051));+#1227=CARTESIAN_POINT('Ctrl Pts',(-0.0486684257848271,0.32904342367623,+-0.515159135022842));+#1228=CARTESIAN_POINT('Ctrl Pts',(-0.0478369876905868,0.329205396013009,+-0.514594994011269));+#1229=CARTESIAN_POINT('Ctrl Pts',(-0.0430617484233418,0.330135659694878,+-0.511354935215078));+#1230=CARTESIAN_POINT('Ctrl Pts',(-0.0379624620523267,0.330947849687595,+-0.508625226976544));+#1231=CARTESIAN_POINT('Ctrl Pts',(-0.0326888097749409,0.331606324820217,+-0.506451366591713));+#1232=CARTESIAN_POINT('Ctrl Pts',(-0.0323552675167673,0.331647971343563,+-0.506313876620139));+#1233=CARTESIAN_POINT('Ctrl Pts',(-0.032021032986856,0.331689007119612,+-0.506178594145299));+#1234=CARTESIAN_POINT('Ctrl Pts',(-0.0316861458748877,0.331729424827186,+-0.506045524125155));+#1235=CARTESIAN_POINT('Ctrl Pts',(-0.0264079329621428,0.332366455212633,+-0.503948184832242));+#1236=CARTESIAN_POINT('Ctrl Pts',(-0.0209701854365,0.332848404642567,-0.502396914393403));+#1237=CARTESIAN_POINT('Ctrl Pts',(-0.0154011785895995,0.333163405219307,+-0.501385774727177));+#1238=CARTESIAN_POINT('Ctrl Pts',(-0.015030218327526,0.333184387900728,+-0.501318421131485));+#1239=CARTESIAN_POINT('Ctrl Pts',(-0.0146586701959157,0.333204625209552,+-0.501253481937854));+#1240=CARTESIAN_POINT('Ctrl Pts',(-0.0142865408610343,0.333224111631104,+-0.501190963145559));+#1241=CARTESIAN_POINT('Ctrl Pts',(-0.0110219332273635,0.333395061660003,+-0.500642499728568));+#1242=CARTESIAN_POINT('Ctrl Pts',(-0.00766076370280286,0.333510107488915,+-0.500275901559432));+#1243=CARTESIAN_POINT('Ctrl Pts',(-0.00432959778476578,0.333563070595375,+-0.500108629035027));+#1244=CARTESIAN_POINT('Ctrl Pts',(-0.00287705978997395,0.33358616488919,+-0.500035690699573));+#1245=CARTESIAN_POINT('Ctrl Pts',(-0.00143032372268609,0.333597623611267,+-0.500000000001246));+#1246=CARTESIAN_POINT('Ctrl Pts',(-4.23474191491434E-17,0.333597712147098,+-0.500000000001248));+#1247=CARTESIAN_POINT('Ctrl Pts',(0.00127664017257952,0.333597791170047,+-0.50000000000125));+#1248=CARTESIAN_POINT('Ctrl Pts',(0.00256728355619576,0.333588821040261,+-0.500028398573773));+#1249=CARTESIAN_POINT('Ctrl Pts',(0.00386503889744944,0.33357035186095,+-0.500086556013059));+#1250=CARTESIAN_POINT('Ctrl Pts',(0.008334416340219,0.333506745316039,-0.500286846113209));+#1251=CARTESIAN_POINT('Ctrl Pts',(0.0127941939934491,0.333332153703138,+-0.500839094638693));+#1252=CARTESIAN_POINT('Ctrl Pts',(0.0171213972212092,0.333059467387661,+-0.501716259588924));+#1253=CARTESIAN_POINT('Ctrl Pts',(0.0186867978887101,0.332960820913761,+-0.502033581084201));+#1254=CARTESIAN_POINT('Ctrl Pts',(0.0202349615821249,0.33284945897161,-0.502392952363184));+#1255=CARTESIAN_POINT('Ctrl Pts',(0.0217606118049948,0.332726389475778,+-0.502791556787527));+#1256=CARTESIAN_POINT('Ctrl Pts',(0.0268339602095553,0.332317138114639,+-0.504117063178164));+#1257=CARTESIAN_POINT('Ctrl Pts',(0.031658259000973,0.331775919681132,-0.505878978317662));+#1258=CARTESIAN_POINT('Ctrl Pts',(0.03614159392476,0.331150545189834,-0.507965490306588));+#1259=CARTESIAN_POINT('Ctrl Pts',(0.0367333426694265,0.331068002935806,+-0.508240885937556));+#1260=CARTESIAN_POINT('Ctrl Pts',(0.0373192154171622,0.33098400412168,-0.508521899577816));+#1261=CARTESIAN_POINT('Ctrl Pts',(0.0378990601104972,0.330898666503696,+-0.508808243912157));+#1262=CARTESIAN_POINT('Ctrl Pts',(0.0428720496772806,0.330166775552959,+-0.511264052171769));+#1263=CARTESIAN_POINT('Ctrl Pts',(0.0473963026943938,0.329335552213608,+-0.51411186040868));+#1264=CARTESIAN_POINT('Ctrl Pts',(0.0514357146595763,0.328469667754751,+-0.517179460546085));+#1265=CARTESIAN_POINT('Ctrl Pts',(0.0554751266247527,0.327603783295895,+-0.520247060683485));+#1266=CARTESIAN_POINT('Ctrl Pts',(0.0590305711095979,0.326703069400534,+-0.523534624941068));+#1267=CARTESIAN_POINT('Ctrl Pts',(0.0621104991631706,0.325825346817421,+-0.526865466368051));+#1268=CARTESIAN_POINT('Ctrl Pts',(0.0642828028044759,0.325206280420353,+-0.529214741687016));+#1269=CARTESIAN_POINT('Ctrl Pts',(0.066222549351971,0.324597097266371,-0.531581638040996));+#1270=CARTESIAN_POINT('Ctrl Pts',(0.0680142686977014,0.32398630359567,-0.534023438788317));+#1271=CARTESIAN_POINT('Ctrl Pts',(0.0687628786927333,0.32373110389161,-0.535043663551574));+#1272=CARTESIAN_POINT('Ctrl Pts',(0.0694852050164163,0.323475507565481,+-0.536077410578652));+#1273=CARTESIAN_POINT('Ctrl Pts',(0.0701854002153471,0.323218240397322,+-0.537130542152087));+#1274=CARTESIAN_POINT('Ctrl Pts',(0.0720490904064754,0.322533479492752,+-0.539933633327769));+#1275=CARTESIAN_POINT('Ctrl Pts',(0.0737570008558326,0.321838991983612,+-0.542869547564704));+#1276=CARTESIAN_POINT('Ctrl Pts',(0.0753242333748941,0.321123478652176,+-0.546020042051158));+#1277=CARTESIAN_POINT('Ctrl Pts',(0.0757550825759678,0.320926776284495,+-0.546886147122337));+#1278=CARTESIAN_POINT('Ctrl Pts',(0.0761750221607496,0.320728456938263,+-0.547768577223222));+#1279=CARTESIAN_POINT('Ctrl Pts',(0.0765836937365764,0.320528231597415,+-0.548669240624152));+#1280=CARTESIAN_POINT('Ctrl Pts',(0.0778445369043568,0.319910491661587,+-0.551447988554531));+#1281=CARTESIAN_POINT('Ctrl Pts',(0.0789953857101391,0.319277422894449,+-0.554403793550181));+#1282=CARTESIAN_POINT('Ctrl Pts',(0.0799722813187012,0.318656591965115,+-0.557426951712225));+#1283=CARTESIAN_POINT('Ctrl Pts',(0.080463802950968,0.318344223039435,-0.558948043240721));+#1284=CARTESIAN_POINT('Ctrl Pts',(0.0809120153593555,0.318035047242435,+-0.560485818444466));+#1285=CARTESIAN_POINT('Ctrl Pts',(0.0813134857293677,0.317733015006252,+-0.562024607131075));+#1286=CARTESIAN_POINT('Ctrl Pts',(0.0822696373082105,0.317013687700779,+-0.565689423620924));+#1287=CARTESIAN_POINT('Ctrl Pts',(0.0829657729657571,0.316329636811078,+-0.569360040407876));+#1288=CARTESIAN_POINT('Ctrl Pts',(0.0834225783649674,0.31567312954369,-0.573104383224986));+#1289=CARTESIAN_POINT('Ctrl Pts',(0.0835387864195512,0.315506118749826,+-0.574056917488423));+#1290=CARTESIAN_POINT('Ctrl Pts',(0.083639250041426,0.315340875675916,-0.575014280117207));+#1291=CARTESIAN_POINT('Ctrl Pts',(0.0837237775861321,0.315177252320795,+-0.575977669345297));+#1292=CARTESIAN_POINT('Origin',(0.00700751792596543,0.337262588819293,-0.589913308934869));+#1293=CARTESIAN_POINT('Ctrl Pts',(0.109116757948058,0.407229284618558,-0.612965980387));+#1294=CARTESIAN_POINT('Ctrl Pts',(0.109350437761226,0.408101389554135,-0.607507495225083));+#1295=CARTESIAN_POINT('Ctrl Pts',(0.109184107042277,0.409019598129749,-0.602131052666539));+#1296=CARTESIAN_POINT('Ctrl Pts',(0.108630396100019,0.409987481639581,-0.596791223188868));+#1297=CARTESIAN_POINT('Ctrl Pts',(0.108379696199731,0.410425703608639,-0.594373545271669));+#1298=CARTESIAN_POINT('Ctrl Pts',(0.108048870902961,0.410874115638989,-0.591963345882477));+#1299=CARTESIAN_POINT('Ctrl Pts',(0.107635601930886,0.41133295057794,-0.589556789561519));+#1300=CARTESIAN_POINT('Ctrl Pts',(0.106751857436914,0.412314134451738,-0.584410550569982));+#1301=CARTESIAN_POINT('Ctrl Pts',(0.105486889495109,0.41334746879785,-0.579281280503128));+#1302=CARTESIAN_POINT('Ctrl Pts',(0.103859669537717,0.414400545062016,-0.574292907612252));+#1303=CARTESIAN_POINT('Ctrl Pts',(0.103045280615941,0.414927587297104,-0.571796333254516));+#1304=CARTESIAN_POINT('Ctrl Pts',(0.102140972876719,0.415459604576563,-0.569334934333956));+#1305=CARTESIAN_POINT('Ctrl Pts',(0.101153832486935,0.415992888097741,-0.566922879002127));+#1306=CARTESIAN_POINT('Ctrl Pts',(0.0987437846644847,0.417294869864738,+-0.561033981373922));+#1307=CARTESIAN_POINT('Ctrl Pts',(0.0958437614954013,0.418600327094814,+-0.555434729195175));+#1308=CARTESIAN_POINT('Ctrl Pts',(0.0924629338731275,0.419893740802593,+-0.550115182354091));+#1309=CARTESIAN_POINT('Ctrl Pts',(0.091692113462856,0.420188635957352,-0.548902338714419));+#1310=CARTESIAN_POINT('Ctrl Pts',(0.0908961079770694,0.420482880673463,+-0.54770412916781));+#1311=CARTESIAN_POINT('Ctrl Pts',(0.0900747072236793,0.420776239756958,+-0.546520633971023));+#1312=CARTESIAN_POINT('Ctrl Pts',(0.0856506328526562,0.422356275321133,+-0.54014631455784));+#1313=CARTESIAN_POINT('Ctrl Pts',(0.0804882886921904,0.423912683694468,+-0.534204885945203));+#1314=CARTESIAN_POINT('Ctrl Pts',(0.0746866283011336,0.425360539551826,+-0.528864342363774));+#1315=CARTESIAN_POINT('Ctrl Pts',(0.0704676662403142,0.426413418989778,+-0.524980704019665));+#1316=CARTESIAN_POINT('Ctrl Pts',(0.0659082590674826,0.4274102728261,-0.521415098607128));+#1317=CARTESIAN_POINT('Ctrl Pts',(0.0611144663499047,0.428314270167142,+-0.518247279861325));+#1318=CARTESIAN_POINT('Ctrl Pts',(0.0593161264690999,0.428653395066009,+-0.517058906662615));+#1319=CARTESIAN_POINT('Ctrl Pts',(0.0574851450379426,0.428979553362164,+-0.515926123371802));+#1320=CARTESIAN_POINT('Ctrl Pts',(0.0556281928021243,0.429291192082261,+-0.514851628010358));+#1321=CARTESIAN_POINT('Ctrl Pts',(0.0488212062596705,0.430433558877103,+-0.510912875400467));+#1322=CARTESIAN_POINT('Ctrl Pts',(0.041665489199516,0.431377310049096,-0.507753849483488));+#1323=CARTESIAN_POINT('Ctrl Pts',(0.0342811699246263,0.432095485640747,+-0.505374684590758));+#1324=CARTESIAN_POINT('Ctrl Pts',(0.0268968506497366,0.432813661232397,+-0.502995519698027));+#1325=CARTESIAN_POINT('Ctrl Pts',(0.0192839788471603,0.433306183802944,+-0.501399334861433));+#1326=CARTESIAN_POINT('Ctrl Pts',(0.0115939515321883,0.433552644029669,+-0.500600454894252));+#1327=CARTESIAN_POINT('Ctrl Pts',(0.00774233795765498,0.433676085665954,+-0.500200329277009));+#1328=CARTESIAN_POINT('Ctrl Pts',(0.00382433573573818,0.433737875049254,+-0.500000000001352));+#1329=CARTESIAN_POINT('Ctrl Pts',(-3.2398094431426E-17,0.433737875150847,+-0.50000000000138));+#1330=CARTESIAN_POINT('Ctrl Pts',(-0.00381122938529555,0.433737875252091,+-0.500000000001408));+#1331=CARTESIAN_POINT('Ctrl Pts',(-0.00771527576773499,0.433676517661284,+-0.500198924534659));+#1332=CARTESIAN_POINT('Ctrl Pts',(-0.0115537389036249,0.433553929803453,+-0.500596284728585));+#1333=CARTESIAN_POINT('Ctrl Pts',(-0.0192438651793872,0.433308332523181,+-0.501392371589411));+#1334=CARTESIAN_POINT('Ctrl Pts',(-0.0268569294575289,0.432816690561329,+-0.502985655842928));+#1335=CARTESIAN_POINT('Ctrl Pts',(-0.0342424468967898,0.432099252456336,+-0.505362216162758));+#1336=CARTESIAN_POINT('Ctrl Pts',(-0.0416279643360508,0.431381814351344,+-0.507738776482587));+#1337=CARTESIAN_POINT('Ctrl Pts',(-0.0487858450791252,0.43043865900395,+-0.510895609930868));+#1338=CARTESIAN_POINT('Ctrl Pts',(-0.0555951124502964,0.429296735371906,+-0.514832494595315));+#1339=CARTESIAN_POINT('Ctrl Pts',(-0.0605382042378659,0.428467771987168,+-0.517690420594201));+#1340=CARTESIAN_POINT('Ctrl Pts',(-0.0652954910053779,0.427535129819114,+-0.520964316649503));+#1341=CARTESIAN_POINT('Ctrl Pts',(-0.0697493105590982,0.426531634471581,+-0.524589584474895));+#1342=CARTESIAN_POINT('Ctrl Pts',(-0.0714307701924751,0.4261527828143,-0.525958239274456));+#1343=CARTESIAN_POINT('Ctrl Pts',(-0.0730692707848219,0.425763944193504,+-0.527376543396521));+#1344=CARTESIAN_POINT('Ctrl Pts',(-0.0746597112527467,0.425367259736066,+-0.528839572752581));+#1345=CARTESIAN_POINT('Ctrl Pts',(-0.0804628813720279,0.423919844735877,+-0.534177847534909));+#1346=CARTESIAN_POINT('Ctrl Pts',(-0.0856260976633972,0.422363990033612,+-0.540116131586299));+#1347=CARTESIAN_POINT('Ctrl Pts',(-0.0900517608771974,0.420784435707682,+-0.546487585016092));+#1348=CARTESIAN_POINT('Ctrl Pts',(-0.0935728551913596,0.419527729201276,+-0.551556766144614));+#1349=CARTESIAN_POINT('Ctrl Pts',(-0.096631282453926,0.418255204505867,+-0.556893938139805));+#1350=CARTESIAN_POINT('Ctrl Pts',(-0.0992305159011457,0.416981365600478,+-0.562509437495612));+#1351=CARTESIAN_POINT('Ctrl Pts',(-0.0998982588041962,0.416654116468772,+-0.563952058913836));+#1352=CARTESIAN_POINT('Ctrl Pts',(-0.100535462258431,0.416326747832582,+-0.565413175876158));+#1353=CARTESIAN_POINT('Ctrl Pts',(-0.101141670763231,0.415999450390684,+-0.566893176961738));+#1354=CARTESIAN_POINT('Ctrl Pts',(-0.103070813256035,0.414957888951093,+-0.571602997033512));+#1355=CARTESIAN_POINT('Ctrl Pts',(-0.104683090770074,0.413919898974301,+-0.576507246781056));+#1356=CARTESIAN_POINT('Ctrl Pts',(-0.105933507647176,0.41291556368399,-0.58148920901895));+#1357=CARTESIAN_POINT('Ctrl Pts',(-0.106605515100266,0.412375807053342,+-0.584166648692216));+#1358=CARTESIAN_POINT('Ctrl Pts',(-0.107173846504871,0.411845857839403,+-0.586866197656916));+#1359=CARTESIAN_POINT('Ctrl Pts',(-0.107637587737012,0.411330742549284,+-0.589568359769692));+#1360=CARTESIAN_POINT('Ctrl Pts',(-0.10854180247563,0.410326357333714,-0.594837105870236));+#1361=CARTESIAN_POINT('Ctrl Pts',(-0.109053044235506,0.409373300007362,+-0.600115548652852));+#1362=CARTESIAN_POINT('Ctrl Pts',(-0.109184952909735,0.40846777989665,-0.605449300611543));+#1363=CARTESIAN_POINT('Ctrl Pts',(-0.109246576175376,0.408044751565484,+-0.607941048904321));+#1364=CARTESIAN_POINT('Ctrl Pts',(-0.109224681686036,0.407632061498369,+-0.610445008964787));+#1365=CARTESIAN_POINT('Ctrl Pts',(-0.109116757946487,0.407229284618979,+-0.612965980387));+#1366=CARTESIAN_POINT('Origin',(0.15535643361,0.5797981035151,-1.65));+#1367=CARTESIAN_POINT('Origin',(-0.1462327604829,-0.5457480918533,-0.4497766934534));+#1368=CARTESIAN_POINT('',(0.08400025863603,0.3134932330786,-0.5869634669142));+#1369=CARTESIAN_POINT('Origin',(0.,0.,-1.112988834673));+#1370=CARTESIAN_POINT('Origin',(0.,0.,-0.5759776693453));+#1371=CARTESIAN_POINT('Origin',(0.,0.,-1.112988834673));+#1372=CARTESIAN_POINT('',(0.08372377758611,-0.3151772523208,-0.5759776693453));+#1373=CARTESIAN_POINT('Origin',(0.,0.,-0.5759776693453));+#1374=CARTESIAN_POINT('',(0.3134932330786,-0.08400025863603,-0.5869634669142));+#1375=CARTESIAN_POINT('',(0.08400025863603,-0.3134932330786,-0.5869634669142));+#1376=CARTESIAN_POINT('',(0.04503506548499,-0.1680731525122,-1.65));+#1377=CARTESIAN_POINT('Origin',(-0.00700757663842628,-0.337262595554786,+-0.589913313526065));+#1378=CARTESIAN_POINT('Origin',(0.,0.,-1.112988834673));+#1379=CARTESIAN_POINT('',(-0.3151772523208,0.08372377758611,-0.5759776693453));+#1380=CARTESIAN_POINT('Origin',(0.,0.,-0.5759776693453));+#1381=CARTESIAN_POINT('',(-0.3134932330786,0.08400025863603,-0.5869634669142));+#1382=CARTESIAN_POINT('Origin',(-0.337261758629422,-0.00700186795583252,+-0.589912855265648));+#1383=CARTESIAN_POINT('',(-0.3134932330786,0.08400025863603,-0.5869634669142));+#1384=CARTESIAN_POINT('Origin',(0.,0.,-1.112988834673));+#1385=CARTESIAN_POINT('',(-0.3151772523208,-0.08372377758611,-0.5759776693453));+#1386=CARTESIAN_POINT('',(-0.08372377758611,-0.3151772523208,-0.5759776693453));+#1387=CARTESIAN_POINT('Origin',(0.,0.,-0.5759776693453));+#1388=CARTESIAN_POINT('',(-0.08400025863603,-0.3134932330786,-0.5869634669142));+#1389=CARTESIAN_POINT('Origin',(0.00700743245344285,-0.337262578962899,+-0.589913302244857));+#1390=CARTESIAN_POINT('',(-0.08400025863603,-0.3134932330786,-0.5869634669142));+#1391=CARTESIAN_POINT('',(-0.3134932330786,-0.08400025863603,-0.5869634669142));+#1392=CARTESIAN_POINT('',(-0.1680731525122,-0.04503506548499,-1.65));+#1393=CARTESIAN_POINT('Origin',(-0.337261758569529,0.00700186771921463,+-0.589912855244523));+#1394=CARTESIAN_POINT('Ctrl Pts',(0.326107911898371,1.07988145098768E-9,+-0.575977675627819));+#1395=CARTESIAN_POINT('Ctrl Pts',(0.32610791209425,0.326107913554983,-0.575977671012111));+#1396=CARTESIAN_POINT('Ctrl Pts',(-3.80851438377974E-10,0.326107913554983,+-0.575977669578031));+#1397=CARTESIAN_POINT('Ctrl Pts',(-0.326107912855953,0.326107913554983,+-0.575977668143951));+#1398=CARTESIAN_POINT('Ctrl Pts',(-0.326107912660074,1.07988133117757E-9,+-0.57597767275966));+#1399=CARTESIAN_POINT('Ctrl Pts',(0.335000000013656,1.85854373458481E-10,+-0.5131904516392));+#1400=CARTESIAN_POINT('Ctrl Pts',(0.335000000214875,0.33500000050547,-0.513190446897633));+#1401=CARTESIAN_POINT('Ctrl Pts',(-1.04740742565666E-10,0.33500000050547,+-0.51319044542445));+#1402=CARTESIAN_POINT('Ctrl Pts',(-0.335000000424357,0.33500000050547,-0.513190443951267));+#1403=CARTESIAN_POINT('Ctrl Pts',(-0.335000000223137,1.85854250381476E-10,+-0.513190448692833));+#1404=CARTESIAN_POINT('Ctrl Pts',(0.3350000004647,-7.11699545517685E-10,+-0.4497766934534));+#1405=CARTESIAN_POINT('Ctrl Pts',(0.335000000665919,0.334999999780095,-0.449776688711833));+#1406=CARTESIAN_POINT('Ctrl Pts',(1.74125175346648E-10,0.334999999780095,+-0.44977668723865));+#1407=CARTESIAN_POINT('Ctrl Pts',(-0.335000000317669,0.334999999780095,+-0.449776685765467));+#1408=CARTESIAN_POINT('Ctrl Pts',(-0.33500000011645,-7.1169966859469E-10,+-0.449776690507034));+#1409=CARTESIAN_POINT('',(0.335,0.,-0.4497766934534));+#1410=CARTESIAN_POINT('Origin',(-0.565,0.,-0.4497766934534));+#1411=CARTESIAN_POINT('',(-0.335,0.,-0.4497766934534));+#1412=CARTESIAN_POINT('Origin',(0.,0.,-0.4497766934534));+#1413=CARTESIAN_POINT('',(-0.3335975848396,0.,-0.5));+#1414=CARTESIAN_POINT('Origin',(0.565,0.,-0.4497766934534));+#1415=CARTESIAN_POINT('Ctrl Pts',(-0.3335975848396,-1.07856518136231E-17,+-0.500000000000804));+#1416=CARTESIAN_POINT('Ctrl Pts',(-0.3335975848396,0.00489738672778759,+-0.500000000000808));+#1417=CARTESIAN_POINT('Ctrl Pts',(-0.333467936944576,0.00970995779359471,+-0.500412423451544));+#1418=CARTESIAN_POINT('Ctrl Pts',(-0.333215435045599,0.0144573198279792,+-0.501219830156154));+#1419=CARTESIAN_POINT('Ctrl Pts',(-0.333161212239588,0.0154767786585698,+-0.501393214424919));+#1420=CARTESIAN_POINT('Ctrl Pts',(-0.333101276089348,0.0164932328007471,+-0.501584998604575));+#1421=CARTESIAN_POINT('Ctrl Pts',(-0.333035616046404,0.0175068508241,-0.501795310687654));+#1422=CARTESIAN_POINT('Ctrl Pts',(-0.332664193783648,0.0232406317192084,+-0.502984992970513));+#1423=CARTESIAN_POINT('Ctrl Pts',(-0.33211194629794,0.0288818654182494,+-0.504770955126313));+#1424=CARTESIAN_POINT('Ctrl Pts',(-0.331402441846782,0.0342819607033563,+-0.507127204103399));+#1425=CARTESIAN_POINT('Ctrl Pts',(-0.330692937395624,0.0396820559884595,+-0.509483453080484));+#1426=CARTESIAN_POINT('Ctrl Pts',(-0.329826121305529,0.0448417786424667,+-0.512409780913939));+#1427=CARTESIAN_POINT('Ctrl Pts',(-0.328846826474115,0.04963378003986,-0.515849012851854));+#1428=CARTESIAN_POINT('Ctrl Pts',(-0.327867531642701,0.0544257814372568,+-0.519288244789772));+#1429=CARTESIAN_POINT('Ctrl Pts',(-0.326775720034377,0.058850415310749,+-0.523239836765332));+#1430=CARTESIAN_POINT('Ctrl Pts',(-0.32562484508521,0.062806437806181,-0.527627682958961));+#1431=CARTESIAN_POINT('Ctrl Pts',(-0.324473970136043,0.0667624603016102,+-0.532015529152587));+#1432=CARTESIAN_POINT('Ctrl Pts',(-0.323264127199919,0.0702497651841301,+-0.536838975202855));+#1433=CARTESIAN_POINT('Ctrl Pts',(-0.322049860451746,0.0731886029859069,+-0.542008881797699));+#1434=CARTESIAN_POINT('Ctrl Pts',(-0.321005863853292,0.0757153432372426,+-0.546453839964815));+#1435=CARTESIAN_POINT('Ctrl Pts',(-0.319959074809438,0.0778347857190225,+-0.551157265417548));+#1436=CARTESIAN_POINT('Ctrl Pts',(-0.31895118750638,0.0794975048584361,+-0.556001802443396));+#1437=CARTESIAN_POINT('Ctrl Pts',(-0.318786806585747,0.079768685279164,+-0.556791920003097));+#1438=CARTESIAN_POINT('Ctrl Pts',(-0.318623469562359,0.0800278236676916,+-0.55758575587927));+#1439=CARTESIAN_POINT('Ctrl Pts',(-0.318461366050339,0.0802748324236238,+-0.558382767208303));+#1440=CARTESIAN_POINT('Ctrl Pts',(-0.317558298056176,0.0816509019422895,+-0.562822864854938));+#1441=CARTESIAN_POINT('Ctrl Pts',(-0.316690987084109,0.0826540312775419,+-0.567359782952332));+#1442=CARTESIAN_POINT('Ctrl Pts',(-0.315863361522256,0.0832839518709352,+-0.572025584907004));+#1443=CARTESIAN_POINT('Ctrl Pts',(-0.315631534673193,0.0834603994253092,+-0.573332526373484));+#1444=CARTESIAN_POINT('Ctrl Pts',(-0.315402807565937,0.0836072561332406,+-0.574649634644197));+#1445=CARTESIAN_POINT('Ctrl Pts',(-0.315177252320794,0.0837237775861357,+-0.575977669345297));+#1446=CARTESIAN_POINT('Origin',(-0.401430300293736,3.27064764160667E-13,+-0.603897806997888));+#1447=CARTESIAN_POINT('',(-0.4074829924761,0.1091847387634,-0.613036359306));+#1448=CARTESIAN_POINT('Ctrl Pts',(-0.315177252320765,-0.0837237775862577,+-0.575977669345281));+#1449=CARTESIAN_POINT('Ctrl Pts',(-0.315982932360323,-0.0833075646592761,+-0.571233948799839));+#1450=CARTESIAN_POINT('Ctrl Pts',(-0.316829077480521,-0.0825083940600536,+-0.566629764112063));+#1451=CARTESIAN_POINT('Ctrl Pts',(-0.317712220903149,-0.0813412205777783,+-0.562131121131347));+#1452=CARTESIAN_POINT('Ctrl Pts',(-0.317959367998807,-0.0810145878600535,+-0.560872178904524));+#1453=CARTESIAN_POINT('Ctrl Pts',(-0.31820940449216,-0.0806588209016876,+-0.55962153372759));+#1454=CARTESIAN_POINT('Ctrl Pts',(-0.318462217244658,-0.0802735359629575,+-0.558378584134861));+#1455=CARTESIAN_POINT('Ctrl Pts',(-0.31945754963054,-0.0787566560843827,+-0.553485049408312));+#1456=CARTESIAN_POINT('Ctrl Pts',(-0.320498977465188,-0.0767785891004961,+-0.548712878370134));+#1457=CARTESIAN_POINT('Ctrl Pts',(-0.321544013587449,-0.0743809761372636,+-0.544182150641016));+#1458=CARTESIAN_POINT('Ctrl Pts',(-0.32171291605479,-0.0739934653741517,+-0.543449878257321));+#1459=CARTESIAN_POINT('Ctrl Pts',(-0.321881921888042,-0.0735950998205491,+-0.542723877701984));+#1460=CARTESIAN_POINT('Ctrl Pts',(-0.322050864047548,-0.0731861735036912,+-0.542004608270453));+#1461=CARTESIAN_POINT('Ctrl Pts',(-0.323265087913333,-0.0702471315574471,+-0.536835063189169));+#1462=CARTESIAN_POINT('Ctrl Pts',(-0.324474825937907,-0.0667598105990379,+-0.532012175889732));+#1463=CARTESIAN_POINT('Ctrl Pts',(-0.325625596190383,-0.06280385623367,+-0.527624819713992));+#1464=CARTESIAN_POINT('Ctrl Pts',(-0.32677636644286,-0.0588479018682992,+-0.523237463538248));+#1465=CARTESIAN_POINT('Ctrl Pts',(-0.327868075100742,-0.0544234085407739,+-0.519286302475132));+#1466=CARTESIAN_POINT('Ctrl Pts',(-0.328847281066494,-0.0496315555383764,+-0.515847416376405));+#1467=CARTESIAN_POINT('Ctrl Pts',(-0.329826487032245,-0.0448397025359821,+-0.512408530277681));+#1468=CARTESIAN_POINT('Ctrl Pts',(-0.33069322895068,-0.0396801295741799,+-0.5094824707284));+#1469=CARTESIAN_POINT('Ctrl Pts',(-0.331402666665915,-0.0342802504191809,+-0.507126457866762));+#1470=CARTESIAN_POINT('Ctrl Pts',(-0.33211210438115,-0.0288803712641782,+-0.504770445005122));+#1471=CARTESIAN_POINT('Ctrl Pts',(-0.332664292384823,-0.0232394241567108,+-0.502984677337351));+#1472=CARTESIAN_POINT('Ctrl Pts',(-0.333035675132521,-0.0175059361046273,+-0.501795120901761));+#1473=CARTESIAN_POINT('Ctrl Pts',(-0.333341428972251,-0.0127856414817898,+-0.500815776949969));+#1474=CARTESIAN_POINT('Ctrl Pts',(-0.333524079573346,-0.00800425093680821,+-0.500234268944126));+#1475=CARTESIAN_POINT('Ctrl Pts',(-0.333579637568279,-0.00314219531358425,+-0.500057197209649));+#1476=CARTESIAN_POINT('Ctrl Pts',(-0.333591562882249,-0.00209857342053004,+-0.500019189430006));+#1477=CARTESIAN_POINT('Ctrl Pts',(-0.3335975848396,-0.00105123145341152,+-0.500000000000803));+#1478=CARTESIAN_POINT('Ctrl Pts',(-0.3335975848396,-1.07856518136231E-17,+-0.500000000000804));+#1479=CARTESIAN_POINT('',(-0.4074829924761,-0.1091847387634,-0.613036359306));+#1480=CARTESIAN_POINT('Ctrl Pts',(-0.411248496524204,0.10771075566218,-0.590000000000004));+#1481=CARTESIAN_POINT('Ctrl Pts',(-0.412335365106895,0.106754178723554,+-0.584287343180426));+#1482=CARTESIAN_POINT('Ctrl Pts',(-0.413450266924472,0.105367062274251,+-0.57876861799284));+#1483=CARTESIAN_POINT('Ctrl Pts',(-0.414588514085221,0.103566593934644,+-0.573407235034872));+#1484=CARTESIAN_POINT('Ctrl Pts',(-0.414919555429589,0.103042955940903,+-0.571847960842692));+#1485=CARTESIAN_POINT('Ctrl Pts',(-0.415252558744554,0.102484019571602,+-0.570302044395784));+#1486=CARTESIAN_POINT('Ctrl Pts',(-0.415587357854052,0.101889411542141,+-0.568768782073528));+#1487=CARTESIAN_POINT('Ctrl Pts',(-0.416868551661604,0.0996139929294409,+-0.562901364458308));+#1488=CARTESIAN_POINT('Ctrl Pts',(-0.418179100460899,0.0968126169863802,+-0.557221600045358));+#1489=CARTESIAN_POINT('Ctrl Pts',(-0.419472455342518,0.0935444334396831,+-0.55185371937497));+#1490=CARTESIAN_POINT('Ctrl Pts',(-0.419679172147034,0.0930220799318057,+-0.550995771448245));+#1491=CARTESIAN_POINT('Ctrl Pts',(-0.419885461010002,0.0924879001153955,+-0.550145747168357));+#1492=CARTESIAN_POINT('Ctrl Pts',(-0.420091146313179,0.0919422448301508,+-0.549304097975733));+#1493=CARTESIAN_POINT('Ctrl Pts',(-0.42158373275419,0.0879826149118071,+-0.543196543602156));+#1494=CARTESIAN_POINT('Ctrl Pts',(-0.423042803560813,0.0834187976153937,+-0.537528124716524));+#1495=CARTESIAN_POINT('Ctrl Pts',(-0.424419963758459,0.0783103373174552,+-0.532361072541459));+#1496=CARTESIAN_POINT('Ctrl Pts',(-0.425647567685791,0.0737566434660921,+-0.52775514974927));+#1497=CARTESIAN_POINT('Ctrl Pts',(-0.426810703038692,0.0687666605271557,+-0.523551336662487));+#1498=CARTESIAN_POINT('Ctrl Pts',(-0.42786246633763,0.063455892915773,-0.519838841449086));+#1499=CARTESIAN_POINT('Ctrl Pts',(-0.427990600333247,0.0628088938480838,+-0.519386556371366));+#1500=CARTESIAN_POINT('Ctrl Pts',(-0.428117093408138,0.0621571752986617,+-0.518941516825114));+#1501=CARTESIAN_POINT('Ctrl Pts',(-0.428241871215455,0.0615009786666518,+-0.518503843689486));+#1502=CARTESIAN_POINT('Ctrl Pts',(-0.429390863694495,0.0554585179826378,+-0.514473614661368));+#1503=CARTESIAN_POINT('Ctrl Pts',(-0.430392859164028,0.0490356625372045,+-0.511067306868829));+#1504=CARTESIAN_POINT('Ctrl Pts',(-0.431211799817661,0.0423348025936602,+-0.508318555235263));+#1505=CARTESIAN_POINT('Ctrl Pts',(-0.432030740471295,0.0356339426501112,+-0.505569803601695));+#1506=CARTESIAN_POINT('Ctrl Pts',(-0.432666700118526,0.0286530521634675,+-0.503483081871434));+#1507=CARTESIAN_POINT('Ctrl Pts',(-0.433093524597879,0.0215733298020928,+-0.502094248778484));+#1508=CARTESIAN_POINT('Ctrl Pts',(-0.433437698864707,0.0158645235575013,+-0.500974349066458));+#1509=CARTESIAN_POINT('Ctrl Pts',(-0.433645286200684,0.0100929345251606,+-0.500301088788609));+#1510=CARTESIAN_POINT('Ctrl Pts',(-0.433713401178734,0.00423523222649463,+-0.500079922611624));+#1511=CARTESIAN_POINT('Ctrl Pts',(-0.433729758349954,0.00282856012299885,+-0.500026811634736));+#1512=CARTESIAN_POINT('Ctrl Pts',(-0.4337380134347,0.00141691441391671,+-0.500000000000854));+#1513=CARTESIAN_POINT('Ctrl Pts',(-0.4337380134347,1.02443375240448E-17,+-0.50000000000086));+#1514=CARTESIAN_POINT('Origin',(-0.5797981035151,0.15535643361,-1.65));+#1515=CARTESIAN_POINT('Ctrl Pts',(0.421594773337783,3.49079407643331E-9,+-0.612965998035337));+#1516=CARTESIAN_POINT('Ctrl Pts',(0.42159477358289,0.421594778195061,-0.612965985033865));+#1517=CARTESIAN_POINT('Ctrl Pts',(-1.1213764284346E-9,0.421594778195061,+-0.612965980994025));+#1518=CARTESIAN_POINT('Ctrl Pts',(-0.421594775825643,0.42159477819506,-0.612965976954184));+#1519=CARTESIAN_POINT('Ctrl Pts',(-0.421594775580536,3.4907939215419E-9,+-0.612965989955655));+#1520=CARTESIAN_POINT('Ctrl Pts',(0.435000000034728,9.83699919706326E-10,+-0.531921933613032));+#1521=CARTESIAN_POINT('Ctrl Pts',(0.435000000287629,0.435000001616118,-0.53192192019816));+#1522=CARTESIAN_POINT('Ctrl Pts',(-3.44789210291156E-10,0.435000001616118,+-0.531921916029866));+#1523=CARTESIAN_POINT('Ctrl Pts',(-0.435000000977207,0.435000001616118,+-0.531921911861573));+#1524=CARTESIAN_POINT('Ctrl Pts',(-0.435000000724307,9.83699759889923E-10,+-0.531921925276445));+#1525=CARTESIAN_POINT('Ctrl Pts',(0.435000001180448,-1.54955962635399E-9,+-0.4497766934534));+#1526=CARTESIAN_POINT('Ctrl Pts',(0.435000001433349,0.434999999441439,-0.449776680038528));+#1527=CARTESIAN_POINT('Ctrl Pts',(4.4234978091698E-10,0.434999999441439,+-0.449776675870234));+#1528=CARTESIAN_POINT('Ctrl Pts',(-0.435000000548649,0.434999999441439,+-0.449776671701941));+#1529=CARTESIAN_POINT('Ctrl Pts',(-0.435000000295749,-1.54955978617039E-9,+-0.449776685116813));+#1530=CARTESIAN_POINT('Origin',(-0.2656296022295,-0.07117523740319,-1.65));+#1531=CARTESIAN_POINT('Ctrl Pts',(-0.326107911898371,-1.07988155587198E-9,+-0.575977675627819));+#1532=CARTESIAN_POINT('Ctrl Pts',(-0.32610791209425,-0.326107913554983,+-0.575977671012111));+#1533=CARTESIAN_POINT('Ctrl Pts',(3.80851438377975E-10,-0.326107913554983,+-0.575977669578031));+#1534=CARTESIAN_POINT('Ctrl Pts',(0.326107912855953,-0.326107913554983,+-0.575977668143951));+#1535=CARTESIAN_POINT('Ctrl Pts',(0.326107912660074,-1.07988137111427E-9,+-0.57597767275966));+#1536=CARTESIAN_POINT('Ctrl Pts',(-0.335000000013656,-1.85854481202705E-10,+-0.5131904516392));+#1537=CARTESIAN_POINT('Ctrl Pts',(-0.335000000214875,-0.33500000050547,+-0.513190446897633));+#1538=CARTESIAN_POINT('Ctrl Pts',(1.04740742565669E-10,-0.33500000050547,+-0.51319044542445));+#1539=CARTESIAN_POINT('Ctrl Pts',(0.335000000424357,-0.33500000050547,-0.513190443951267));+#1540=CARTESIAN_POINT('Ctrl Pts',(0.335000000223137,-1.8585429140715E-10,+-0.513190448692834));+#1541=CARTESIAN_POINT('Ctrl Pts',(-0.3350000004647,7.11699437773466E-10,+-0.4497766934534));+#1542=CARTESIAN_POINT('Ctrl Pts',(-0.335000000665919,-0.334999999780095,+-0.449776688711833));+#1543=CARTESIAN_POINT('Ctrl Pts',(-1.74125175346649E-10,-0.334999999780095,+-0.44977668723865));+#1544=CARTESIAN_POINT('Ctrl Pts',(0.335000000317669,-0.334999999780095,+-0.449776685765467));+#1545=CARTESIAN_POINT('Ctrl Pts',(0.33500000011645,7.1169962756902E-10,+-0.449776690507034));+#1546=CARTESIAN_POINT('Origin',(0.,0.,-0.4497766934534));+#1547=CARTESIAN_POINT('Ctrl Pts',(0.0837237775861505,-0.315177252320791,+-0.575977669345295));+#1548=CARTESIAN_POINT('Ctrl Pts',(0.0833111158288333,-0.315976058218939,+-0.571274422690266));+#1549=CARTESIAN_POINT('Ctrl Pts',(0.0825222653094782,-0.316814349478257,+-0.566710297403623));+#1550=CARTESIAN_POINT('Ctrl Pts',(0.081371829388118,-0.317689089762647,+-0.562249395052968));+#1551=CARTESIAN_POINT('Ctrl Pts',(0.0809974744755795,-0.31797373258071,+-0.560797805548228));+#1552=CARTESIAN_POINT('Ctrl Pts',(0.0805844192922588,-0.318262225002428,+-0.559357184135653));+#1553=CARTESIAN_POINT('Ctrl Pts',(0.0801320089383487,-0.318554400391273,+-0.5579265335182));+#1554=CARTESIAN_POINT('Ctrl Pts',(0.0786476942517575,-0.319512999629718,+-0.553232706985467));+#1555=CARTESIAN_POINT('Ctrl Pts',(0.0767358952255177,-0.32051462386449,+-0.548647889163378));+#1556=CARTESIAN_POINT('Ctrl Pts',(0.074437164903471,-0.321519327508318,+-0.544288562317644));+#1557=CARTESIAN_POINT('Ctrl Pts',(0.073882109826671,-0.321761924846875,+-0.543235952329448));+#1558=CARTESIAN_POINT('Ctrl Pts',(0.07330472111427,-0.322004721617662,-0.542196412445231));+#1559=CARTESIAN_POINT('Ctrl Pts',(0.072705987626208,-0.322247201405081,+-0.541171405006137));+#1560=CARTESIAN_POINT('Ctrl Pts',(0.0696276320585324,-0.323493898004706,+-0.535901385172677));+#1561=CARTESIAN_POINT('Ctrl Pts',(0.0659867117412812,-0.3247298119796,-0.531011949183155));+#1562=CARTESIAN_POINT('Ctrl Pts',(0.0618170704505808,-0.325908710579265,+-0.526549788856714));+#1563=CARTESIAN_POINT('Ctrl Pts',(0.0583291747335857,-0.326894856650322,+-0.522817201613333));+#1564=CARTESIAN_POINT('Ctrl Pts',(0.0544671658224409,-0.327841935320495,+-0.519388553988434));+#1565=CARTESIAN_POINT('Ctrl Pts',(0.0503267876586513,-0.328703861003485,+-0.51635182076558));+#1566=CARTESIAN_POINT('Ctrl Pts',(0.0495175075896181,-0.328872333362443,+-0.515758259604013));+#1567=CARTESIAN_POINT('Ctrl Pts',(0.048697707444218,-0.329037580393907,+-0.515179563557084));+#1568=CARTESIAN_POINT('Ctrl Pts',(0.0478682583864593,-0.329199301338863,+-0.514616221947355));+#1569=CARTESIAN_POINT('Ctrl Pts',(0.0430885501137471,-0.330131219859051,+-0.511369960435331));+#1570=CARTESIAN_POINT('Ctrl Pts',(0.0379837795073046,-0.330944869732061,+-0.508635202763097));+#1571=CARTESIAN_POINT('Ctrl Pts',(0.0327038596836068,-0.331604453805711,+-0.506457571848595));+#1572=CARTESIAN_POINT('Ctrl Pts',(0.0323798582626379,-0.33164492907508,+-0.506323941881815));+#1573=CARTESIAN_POINT('Ctrl Pts',(0.0320552022114266,-0.331684828084336,+-0.506192394799861));+#1574=CARTESIAN_POINT('Ctrl Pts',(0.0317299278341516,-0.331724144107688,+-0.50606293517134));+#1575=CARTESIAN_POINT('Ctrl Pts',(0.0264357949832631,-0.332364047725485,+-0.503955863357209));+#1576=CARTESIAN_POINT('Ctrl Pts',(0.0209804384401526,-0.332847979990919,+-0.502398231947721));+#1577=CARTESIAN_POINT('Ctrl Pts',(0.0153931995384828,-0.333163848411464,+-0.501384326428245));+#1578=CARTESIAN_POINT('Ctrl Pts',(0.0150430239408443,-0.333183645204636,+-0.50132078073466));+#1579=CARTESIAN_POINT('Ctrl Pts',(0.0146923254682716,-0.333202777757167,+-0.501259386577382));+#1580=CARTESIAN_POINT('Ctrl Pts',(0.0143411099383942,-0.333221241426901,+-0.501200148983153));+#1581=CARTESIAN_POINT('Ctrl Pts',(0.0110667058138217,-0.333393379362701,+-0.500647873225472));+#1582=CARTESIAN_POINT('Ctrl Pts',(0.0076956806714656,-0.333509258668981,+-0.500278623506273));+#1583=CARTESIAN_POINT('Ctrl Pts',(0.00435443160267475,-0.333562682635347,+-0.500109879633224));+#1584=CARTESIAN_POINT('Ctrl Pts',(0.00289360381057889,-0.333586040132266,+-0.500036103110007));+#1585=CARTESIAN_POINT('Ctrl Pts',(0.00143856591257334,-0.333597628343911,+-0.500000000000339));+#1586=CARTESIAN_POINT('Ctrl Pts',(2.93270070027646E-17,-0.333597713908903,+-0.50000000000034));+#1587=CARTESIAN_POINT('Ctrl Pts',(-0.00125511588470852,-0.333597788562403,+-0.500000000000341));+#1588=CARTESIAN_POINT('Ctrl Pts',(-0.00252386067423867,-0.333589116858328,+-0.500027445011607));+#1589=CARTESIAN_POINT('Ctrl Pts',(-0.00379977072823687,-0.333571264107733,+-0.500083655844876));+#1590=CARTESIAN_POINT('Ctrl Pts',(-0.00827914637985656,-0.333508587921369,+-0.500280996901259));+#1591=CARTESIAN_POINT('Ctrl Pts',(-0.0127493477274121,-0.333334440970801,+-0.500831842599127));+#1592=CARTESIAN_POINT('Ctrl Pts',(-0.0170872634537396,-0.333061637479901,+-0.501709347446107));+#1593=CARTESIAN_POINT('Ctrl Pts',(-0.0186403932013131,-0.332963964024792,+-0.50202352572145));+#1594=CARTESIAN_POINT('Ctrl Pts',(-0.0201766758888196,-0.332853763737257,+-0.502379114998292));+#1595=CARTESIAN_POINT('Ctrl Pts',(-0.0216909860379876,-0.332732009414287,+-0.502773396916482));+#1596=CARTESIAN_POINT('Ctrl Pts',(-0.0267562157220168,-0.332324752280599,+-0.504092234062882));+#1597=CARTESIAN_POINT('Ctrl Pts',(-0.0315755298036749,-0.331785759560259,+-0.505846607674932));+#1598=CARTESIAN_POINT('Ctrl Pts',(-0.0360562914756588,-0.331162420758599,+-0.507925849167792));+#1599=CARTESIAN_POINT('Ctrl Pts',(-0.0366565646304176,-0.331078914074613,+-0.50820439842834));+#1600=CARTESIAN_POINT('Ctrl Pts',(-0.0372508270530885,-0.330993903647512,+-0.508488739393776));+#1601=CARTESIAN_POINT('Ctrl Pts',(-0.037838917703469,-0.330907511812341,+-0.508778573543225));+#1602=CARTESIAN_POINT('Ctrl Pts',(-0.0428168332610387,-0.330176244855344,+-0.511231885665634));+#1603=CARTESIAN_POINT('Ctrl Pts',(-0.0473472492205696,-0.329345118465104,+-0.5140786908808));+#1604=CARTESIAN_POINT('Ctrl Pts',(-0.0513936421593418,-0.328478679793113,+-0.517147531179651));+#1605=CARTESIAN_POINT('Ctrl Pts',(-0.0554400350981078,-0.327612241121124,+-0.520216371478497));+#1606=CARTESIAN_POINT('Ctrl Pts',(-0.0590031424215731,-0.326710333133054,+-0.523507601757999));+#1607=CARTESIAN_POINT('Ctrl Pts',(-0.0620885878272953,-0.325831595671887,+-0.526841778556864));+#1608=CARTESIAN_POINT('Ctrl Pts',(-0.0642649071387008,-0.325211778091306,+-0.529193540588223));+#1609=CARTESIAN_POINT('Ctrl Pts',(-0.0662076623982693,-0.324601923187377,+-0.531562698654379));+#1610=CARTESIAN_POINT('Ctrl Pts',(-0.0680015401083481,-0.323990644618283,+-0.534006096246686));+#1611=CARTESIAN_POINT('Ctrl Pts',(-0.0687509067236727,-0.323735291786945,+-0.535026790451956));+#1612=CARTESIAN_POINT('Ctrl Pts',(-0.0694738521432972,-0.32347957482299,+-0.536060886706243));+#1613=CARTESIAN_POINT('Ctrl Pts',(-0.0701745418453286,-0.323222227201006,+-0.537114215206616));+#1614=CARTESIAN_POINT('Ctrl Pts',(-0.072033378926197,-0.322539518006008,+-0.539908556366695));+#1615=CARTESIAN_POINT('Ctrl Pts',(-0.0737365909720407,-0.321847414119789,+-0.542833725944208));+#1616=CARTESIAN_POINT('Ctrl Pts',(-0.075300166211154,-0.32113446084313,+-0.545971700982215));+#1617=CARTESIAN_POINT('Ctrl Pts',(-0.0757368992613711,-0.320935320916001,+-0.546848190565556));+#1618=CARTESIAN_POINT('Ctrl Pts',(-0.0761624527948228,-0.320734525318884,+-0.547741395009664));+#1619=CARTESIAN_POINT('Ctrl Pts',(-0.0765764688546511,-0.320531766745636,+-0.548653323627318));+#1620=CARTESIAN_POINT('Ctrl Pts',(-0.0778389153986709,-0.319913501212234,+-0.551434039747917));+#1621=CARTESIAN_POINT('Ctrl Pts',(-0.0789913370857365,-0.319279815347515,+-0.554392348560699));+#1622=CARTESIAN_POINT('Ctrl Pts',(-0.0799696098400156,-0.318658285490801,+-0.557418687928551));+#1623=CARTESIAN_POINT('Ctrl Pts',(-0.0804607539645729,-0.318346244972421,+-0.558938068687073));+#1624=CARTESIAN_POINT('Ctrl Pts',(-0.0809087283260045,-0.318037363319987,+-0.560474230464428));+#1625=CARTESIAN_POINT('Ctrl Pts',(-0.0813100968126008,-0.317735567303609,+-0.562011619807543));+#1626=CARTESIAN_POINT('Ctrl Pts',(-0.0822676972353884,-0.317015530722435,+-0.565679582625054));+#1627=CARTESIAN_POINT('Ctrl Pts',(-0.0829651131575208,-0.316330591452659,+-0.56935459406944));+#1628=CARTESIAN_POINT('Ctrl Pts',(-0.083422590702711,-0.315673112071725,+-0.573104483991684));+#1629=CARTESIAN_POINT('Ctrl Pts',(-0.0835387866656639,-0.315506117099856,+-0.574056928707416));+#1630=CARTESIAN_POINT('Ctrl Pts',(-0.0836392483192254,-0.315340879009581,+-0.57501426048914));+#1631=CARTESIAN_POINT('Ctrl Pts',(-0.0837237775860824,-0.315177252320807,+-0.575977669345304));+#1632=CARTESIAN_POINT('Origin',(-1.39731975989932E-13,-0.401200492639971,+-0.603838328400014));+#1633=CARTESIAN_POINT('',(-0.1091167579479,-0.4072292846186,-0.612965980387));+#1634=CARTESIAN_POINT('',(0.1091167579479,-0.4072292846186,-0.612965980387));+#1635=CARTESIAN_POINT('Origin',(-0.15535643361,-0.5797981035151,-1.65));+#1636=CARTESIAN_POINT('Origin',(0.07117523740319,-0.2656296022295,-1.65));+#1637=CARTESIAN_POINT('Origin',(0.,0.,0.0825));+#1638=CARTESIAN_POINT('',(-0.335,4.10256677714363E-17,0.0825));+#1639=CARTESIAN_POINT('Origin',(0.5797981035151,-0.15535643361,-1.65));+#1640=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.01),#1644,+'DISTANCE_ACCURACY_VALUE',+'Maximum model space distance between geometric entities at asserted c+onnectivities');+#1641=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.01),#1644,+'DISTANCE_ACCURACY_VALUE',+'Maximum model space distance between geometric entities at asserted c+onnectivities');+#1642=(+GEOMETRIC_REPRESENTATION_CONTEXT(3)+GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#1640))+GLOBAL_UNIT_ASSIGNED_CONTEXT((#1644,#1646,#1647))+REPRESENTATION_CONTEXT('','3D')+);+#1643=(+GEOMETRIC_REPRESENTATION_CONTEXT(3)+GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#1641))+GLOBAL_UNIT_ASSIGNED_CONTEXT((#1644,#1646,#1647))+REPRESENTATION_CONTEXT('','3D')+);+#1644=(+LENGTH_UNIT()+NAMED_UNIT(*)+SI_UNIT(.MILLI.,.METRE.)+);+#1645=(+LENGTH_UNIT()+NAMED_UNIT(*)+SI_UNIT($,.METRE.)+);+#1646=(+NAMED_UNIT(*)+PLANE_ANGLE_UNIT()+SI_UNIT($,.RADIAN.)+);+#1647=(+NAMED_UNIT(*)+SI_UNIT($,.STERADIAN.)+SOLID_ANGLE_UNIT()+);+#1648=SHAPE_DEFINITION_REPRESENTATION(#1649,#1650);+#1649=PRODUCT_DEFINITION_SHAPE('',$,#1652);+#1650=SHAPE_REPRESENTATION('',(#598),#1642);+#1651=PRODUCT_DEFINITION_CONTEXT('part definition',#1656,'design');+#1652=PRODUCT_DEFINITION('MediumMachineContact',+'MediumMachineContact v1',#1653,#1651);+#1653=PRODUCT_DEFINITION_FORMATION('',$,#1658);+#1654=PRODUCT_RELATED_PRODUCT_CATEGORY('MediumMachineContact v1',+'MediumMachineContact v1',(#1658));+#1655=APPLICATION_PROTOCOL_DEFINITION('international standard',+'automotive_design',2009,#1656);+#1656=APPLICATION_CONTEXT(+'Core Data for Automotive Mechanical Design Process');+#1657=PRODUCT_CONTEXT('part definition',#1656,'mechanical');+#1658=PRODUCT('MediumMachineContact','MediumMachineContact v1',$,(#1657));+#1659=PRESENTATION_STYLE_ASSIGNMENT((#1660));+#1660=SURFACE_STYLE_USAGE(.BOTH.,#1661);+#1661=SURFACE_SIDE_STYLE('',(#1662));+#1662=SURFACE_STYLE_FILL_AREA(#1663);+#1663=FILL_AREA_STYLE('Steel - Satin',(#1664));+#1664=FILL_AREA_STYLE_COLOUR('Steel - Satin',#1665);+#1665=COLOUR_RGB('Steel - Satin',0.627450980392157,0.627450980392157,0.627450980392157);+ENDSEC;+END-ISO-10303-21;
tour-pack-rp2040/AdomRP2040.kicad_symadded+1443
@@ -0,0 +1,1443 @@+(kicad_symbol_lib+	(version 20231120)+	(generator "kicad_symbol_editor")+	(generator_version "9.0")+	(symbol "RP2040_AdomTour"+		(exclude_from_sim no)+		(in_bom yes)+		(on_board yes)+		(in_pos_files yes)+		(duplicate_pin_numbers_are_jumpers no)+		(property "Reference" "U"+			(at 17.526 47.244 0)+			(show_name no)+			(do_not_autoplace no)+			(effects+				(font+					(size 1.27 1.27)+				)+				(justify left)+			)+		)+		(property "Value" "RP2040"+			(at 17.526 44.704 0)+			(show_name no)+			(do_not_autoplace no)+			(effects+				(font+					(size 1.27 1.27)+				)+				(justify left)+			)+		)+		(property "Footprint" "AdomRP2040:QFN-56_AdomRP2040"+			(at 0 0 0)+			(show_name no)+			(do_not_autoplace no)+			(hide yes)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "Datasheet" "https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf"+			(at 0 0 0)+			(show_name no)+			(do_not_autoplace no)+			(hide yes)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "Description" "A microcontroller by Raspberry Pi, Dual ARM Cortex-M0+, 133MHz, no flash, 264KB SRAM, 8 PIO state machines, VQFN-56"+			(at 0 0 0)+			(show_name no)+			(do_not_autoplace no)+			(hide yes)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property private "KLC_S4.2_DVDD" "Not a standalone power converter; internal on-chip voltage regulator for digital core supply; DVDD is the digital core power supply, should be placed next to voltage regulator output."+			(at 0 -2.54 0)+			(show_name yes)+			(do_not_autoplace no)+			(hide yes)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property private "KLC_S4.2_VREG_VOUT" "Should be placed next to DVDD."+			(at 0 -2.54 0)+			(show_name yes)+			(do_not_autoplace no)+			(hide yes)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "ki_keywords" "RPi Pico USB QSPI I2C UART SC0914"+			(at 0 0 0)+			(show_name no)+			(do_not_autoplace no)+			(hide yes)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "ki_fp_filters" "*QFN*7x7mm?P0.4mm?EP3.2x3.2mm*"+			(at 0 0 0)+			(show_name no)+			(do_not_autoplace no)+			(hide yes)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(symbol "RP2040_AdomTour_0_1"+			(rectangle+				(start -21.59 41.91)+				(end 21.59 -41.91)+				(stroke+					(width 0.254)+					(type default)+				)+				(fill+					(type background)+				)+			)+		)+		(symbol "RP2040_AdomTour_1_1"+			(pin power_in line+				(at -2.54 45.72 270)+				(length 3.81)+				(name "IOVDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "1"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 38.1 180)+				(length 3.81)+				(name "GPIO0"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "2"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 35.56 180)+				(length 3.81)+				(name "GPIO1"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "3"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 33.02 180)+				(length 3.81)+				(name "GPIO2"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "4"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 30.48 180)+				(length 3.81)+				(name "GPIO3"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "5"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 27.94 180)+				(length 3.81)+				(name "GPIO4"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "6"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 25.4 180)+				(length 3.81)+				(name "GPIO5"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "7"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 22.86 180)+				(length 3.81)+				(name "GPIO6"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "8"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 20.32 180)+				(length 3.81)+				(name "GPIO7"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "9"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin passive line+				(at -2.54 45.72 270)+				(length 3.81)+				(hide yes)+				(name "IOVDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "10"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 17.78 180)+				(length 3.81)+				(name "GPIO8"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "11"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 15.24 180)+				(length 3.81)+				(name "GPIO9"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "12"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 12.7 180)+				(length 3.81)+				(name "GPIO10"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "13"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 10.16 180)+				(length 3.81)+				(name "GPIO11"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "14"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 7.62 180)+				(length 3.81)+				(name "GPIO12"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "15"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 5.08 180)+				(length 3.81)+				(name "GPIO13"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "16"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 2.54 180)+				(length 3.81)+				(name "GPIO14"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "17"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 0 180)+				(length 3.81)+				(name "GPIO15"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "18"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin input line+				(at -25.4 30.48 0)+				(length 3.81)+				(name "TESTEN"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "19"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin input line+				(at -25.4 -15.24 0)+				(length 3.81)+				(name "XIN"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "20"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin output line+				(at -25.4 -25.4 0)+				(length 3.81)+				(name "XOUT"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "21"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin passive line+				(at -2.54 45.72 270)+				(length 3.81)+				(hide yes)+				(name "IOVDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "22"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin power_in line+				(at 12.7 45.72 270)+				(length 3.81)+				(name "DVDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "23"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin input line+				(at -25.4 -33.02 0)+				(length 3.81)+				(name "SWCLK"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "24"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at -25.4 -35.56 0)+				(length 3.81)+				(name "SWDIO"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "25"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin input line+				(at -25.4 22.86 0)+				(length 3.81)+				(name "RUN"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "26"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -2.54 180)+				(length 3.81)+				(name "GPIO16"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "27"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -5.08 180)+				(length 3.81)+				(name "GPIO17"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "28"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -7.62 180)+				(length 3.81)+				(name "GPIO18"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "29"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -10.16 180)+				(length 3.81)+				(name "GPIO19"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "30"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -12.7 180)+				(length 3.81)+				(name "GPIO20"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "31"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -15.24 180)+				(length 3.81)+				(name "GPIO21"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "32"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin passive line+				(at -2.54 45.72 270)+				(length 3.81)+				(hide yes)+				(name "IOVDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "33"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -17.78 180)+				(length 3.81)+				(name "GPIO22"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "34"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -20.32 180)+				(length 3.81)+				(name "GPIO23"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "35"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -22.86 180)+				(length 3.81)+				(name "GPIO24"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "36"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -25.4 180)+				(length 3.81)+				(name "GPIO25"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "37"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -30.48 180)+				(length 3.81)+				(name "GPIO26/ADC0"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "38"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -33.02 180)+				(length 3.81)+				(name "GPIO27/ADC1"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "39"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -35.56 180)+				(length 3.81)+				(name "GPIO28/ADC2"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "40"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at 25.4 -38.1 180)+				(length 3.81)+				(name "GPIO29/ADC3"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "41"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin passive line+				(at -2.54 45.72 270)+				(length 3.81)+				(hide yes)+				(name "IOVDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "42"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin power_in line+				(at -10.16 45.72 270)+				(length 3.81)+				(name "ADC_AVDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "43"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin power_in line+				(at 2.54 45.72 270)+				(length 3.81)+				(name "VREG_VIN"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "44"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin power_out line+				(at 7.62 45.72 270)+				(length 3.81)+				(name "VREG_VOUT"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "45"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at -25.4 15.24 0)+				(length 3.81)+				(name "USB_DM"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "46"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at -25.4 12.7 0)+				(length 3.81)+				(name "USB_DP"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "47"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin power_in line+				(at -12.7 45.72 270)+				(length 3.81)+				(name "USB_VDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "48"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin passive line+				(at -2.54 45.72 270)+				(length 3.81)+				(hide yes)+				(name "IOVDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "49"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin passive line+				(at 12.7 45.72 270)+				(length 3.81)+				(hide yes)+				(name "DVDD"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "50"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at -25.4 -7.62 0)+				(length 3.81)+				(name "QSPI_SD3"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "51"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin output line+				(at -25.4 2.54 0)+				(length 3.81)+				(name "QSPI_SCLK"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "52"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at -25.4 0 0)+				(length 3.81)+				(name "QSPI_SD0"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "53"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at -25.4 -5.08 0)+				(length 3.81)+				(name "QSPI_SD2"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "54"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin bidirectional line+				(at -25.4 -2.54 0)+				(length 3.81)+				(name "QSPI_SD1"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "55"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin output line+				(at -25.4 5.08 0)+				(length 3.81)+				(name "~{QSPI_SS}"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "56"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin power_in line+				(at 0 -45.72 90)+				(length 3.81)+				(name "GND"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "57"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+		)+		(embedded_fonts no)+	)+	(symbol "ADOM_MECHANICAL_PIN"+		(exclude_from_sim no)+		(in_bom yes)+		(on_board yes)+		(property "Reference" "X"+			(at 0 3.81 0)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "Value" "ADOM_MECHANICAL_PIN"+			(at 0 6.35 0)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "Footprint" "Adom Footprint Library:ADOM_MEDIUM_PIN_v2"+			(at 0 -2.54 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "Datasheet" ""+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "Description" "Adom Mechanical Pin"+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "ki_keywords" "Adom Mechanical Pin"+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "ki_fp_filters" "Adom?Footprint?Library:ADOM?MEDIUM?PIN*"+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(symbol "ADOM_MECHANICAL_PIN_0_0"+			(text ""+				(at 0 0 0)+				(effects+					(font+						(size 1.27 1.27)+					)+				)+			)+			(text ""+				(at 0 0 0)+				(effects+					(font+						(size 1.27 1.27)+					)+				)+			)+		)+		(symbol "ADOM_MECHANICAL_PIN_0_1"+			(rectangle+				(start 2.54 -1.27)+				(end 12.7 -1.27)+				(stroke+					(width 0)+					(type default)+				)+				(fill+					(type none)+				)+			)+			(rectangle+				(start 2.54 1.27)+				(end 2.54 -1.27)+				(stroke+					(width 0)+					(type default)+				)+				(fill+					(type none)+				)+			)+			(rectangle+				(start 2.54 1.27)+				(end 12.7 1.27)+				(stroke+					(width 0)+					(type default)+				)+				(fill+					(type none)+				)+			)+			(rectangle+				(start 12.7 1.27)+				(end 12.7 -1.27)+				(stroke+					(width 0)+					(type default)+				)+				(fill+					(type none)+				)+			)+		)+		(symbol "ADOM_MECHANICAL_PIN_1_1"+			(pin bidirectional line+				(at 0 0 0)+				(length 2.54)+				(name "MECH_PIN"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "1"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+		)+	)+	(symbol "ADOM_SIGNAL_PIN"+		(exclude_from_sim no)+		(in_bom yes)+		(on_board yes)+		(property "Reference" "X"+			(at 0 3.81 0)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "Value" ""+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "Footprint" "Adom Footprint Library:ADOM_MEDIUM_CONTACT"+			(at 0 -2.54 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "Datasheet" ""+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "Description" "Adom Signal Pin"+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "ki_keywords" "Adom Signal Pin"+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(symbol "ADOM_SIGNAL_PIN_0_1"+			(rectangle+				(start 2.54 -1.27)+				(end 11.43 -1.27)+				(stroke+					(width 0)+					(type default)+				)+				(fill+					(type none)+				)+			)+			(rectangle+				(start 2.54 1.27)+				(end 2.54 -1.27)+				(stroke+					(width 0)+					(type default)+				)+				(fill+					(type none)+				)+			)+			(rectangle+				(start 2.54 1.27)+				(end 11.43 1.27)+				(stroke+					(width 0)+					(type default)+				)+				(fill+					(type none)+				)+			)+			(rectangle+				(start 10.16 2.54)+				(end 10.16 2.54)+				(stroke+					(width 0)+					(type default)+				)+				(fill+					(type none)+				)+			)+			(rectangle+				(start 11.43 1.27)+				(end 11.43 -1.27)+				(stroke+					(width 0)+					(type default)+				)+				(fill+					(type none)+				)+			)+		)+		(symbol "ADOM_SIGNAL_PIN_1_1"+			(pin bidirectional line+				(at 0 0 0)+				(length 2.54)+				(name "SIG_PIN"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "1"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+		)+	)+)
tour-pack-rp2040/AdomRP2040.pretty/ADOM_MEDIUM_CONTACT.kicad_modadded+149
@@ -0,0 +1,149 @@+(footprint "ADOM_MEDIUM_CONTACT"+	(version 20240108)+	(generator "pcbnew")+	(generator_version "8.0")+	(layer "F.Cu")+	(property "Reference" "PIN**"+		(at 0 -1.4 0)+		(unlocked yes)+		(layer "F.SilkS")+		(hide yes)+		(uuid "c4131164-c3b6-494b-b3f0-321d3f28f2c5")+		(effects+			(font+				(size 0.5 0.5)+				(thickness 0.1)+			)+		)+	)+	(property "Value" "ADOM_MEDIUM_CONTACT"+		(at 0 -2 0)+		(unlocked yes)+		(layer "F.Fab")+		(hide yes)+		(uuid "c546103f-091e-4361-99e9-be89cecc214d")+		(effects+			(font+				(size 0.25 0.25)+				(thickness 0.0625)+				(bold yes)+			)+		)+	)+	(property "Footprint" "ADOM_MEDIUM_CONTACT"+		(at 0 0 0)+		(unlocked yes)+		(layer "F.Fab")+		(hide yes)+		(uuid "140b8eac-d429-4fe2-81b9-9521a6b871f3")+		(effects+			(font+				(size 1.27 1.27)+				(thickness 0.15)+			)+		)+	)+	(property "Datasheet" ""+		(at 0 0 0)+		(unlocked yes)+		(layer "F.Fab")+		(hide yes)+		(uuid "2da50a77-73dd-4685-944e-bd6128628302")+		(effects+			(font+				(size 1.27 1.27)+				(thickness 0.15)+			)+		)+	)+	(property "Description" ""+		(at 0 0 0)+		(unlocked yes)+		(layer "F.Fab")+		(hide yes)+		(uuid "47c34520-281d-4fd5-849b-c86e1de27709")+		(effects+			(font+				(size 1.27 1.27)+				(thickness 0.15)+			)+		)+	)+	(attr through_hole exclude_from_pos_files)+	(fp_circle+		(center 0 0)+		(end 0.7 0)+		(stroke+			(width 0.05)+			(type default)+		)+		(fill none)+		(layer "F.SilkS")+		(uuid "26defdd4-9e05-4540-8665-d1cb194026ff")+	)+	(fp_circle+		(center 0 0)+		(end 0.99 0)+		(stroke+			(width 0.05)+			(type default)+		)+		(fill none)+		(layer "B.CrtYd")+		(uuid "32403fb1-e538-43c4-8915-29a4118cf417")+	)+	(fp_circle+		(center 0 0)+		(end 0.99 0)+		(stroke+			(width 0.05)+			(type default)+		)+		(fill none)+		(layer "F.CrtYd")+		(uuid "d5ab32ae-26e9-4e56-bbc0-e685eedc629b")+	)+	(fp_rect+		(start -1 -1)+		(end 1 1)+		(stroke+			(width 0.05)+			(type default)+		)+		(fill none)+		(layer "F.Fab")+		(uuid "659f8f63-c4ff-4ab8-8022-63141418426e")+	)+	(fp_text user "${REFERENCE}"+		(at 0 0 0)+		(unlocked yes)+		(layer "F.Fab")+		(uuid "732c3009-44ae-4e09-a044-0af78dee890a")+		(effects+			(font+				(size 0.25 0.25)+				(thickness 0.0625)+				(bold yes)+			)+		)+	)+	(pad "1" thru_hole circle+		(at 0 0)+		(size 1.3 1.3)+		(drill 0.78)+		(layers "*.Cu" "*.Mask")+		(remove_unused_layers no)+		(uuid "739bcfb0-181d-4bd4-9584-ab2d39f99f1b")+	)+	(model "${ADOM_LIBRARY_DIR}/Adom Footprint Library.pretty/3D_Models/MediumMachineContact v1.step"+		(offset+			(xyz 0 0 0)+		)+		(scale+			(xyz 1 1 1)+		)+		(rotate+			(xyz 0 0 0)+		)+	)+)
tour-pack-rp2040/generate_breakout.pyadded+514
@@ -0,0 +1,514 @@+#!/usr/bin/env python3+"""Generate the RP2040 breakout (Adom-molecule design rules).++v3: replaces the v2 standard pin headers with Adom Medium Contacts around the+perimeter + 4 Adom Medium Pins in the corners (per the adom-molecules and+kicad-to-molecule skills). 40×24 mm board on the 8 mm medium-grid; corner+machine pins are 1 mm in from each corner. RP2040 + 3× decoupling caps + 12+MHz crystal + 2× crystal load caps + USB-C receptacle, all placed without+overlap. USB-C sits on the LEFT short edge with its opening facing outward+(pin 1 in the +X direction toward the chip).++No nets are emitted — the goal is geometric placement + clean 3D render.+A user can connect the schematic in KiCad later if they want a working+breakout.++Run:+    python plugins/kicad/tour-pack-rp2040/generate_breakout.py+"""+from __future__ import annotations+import re+import sys+import uuid+from pathlib import Path++HERE = Path(__file__).parent+KICAD_ROOT = Path(r"C:/Program Files/KiCad/10.0/share/kicad")+KICAD_VERSION = "10"++# ── Board geometry ──────────────────────────────────────────────────────+# 40×24 mm — both fit the medium-board class (≤60 mm). Centred at+# (130, 110) so the board outline is (110, 98) → (150, 122).+BOARD_W, BOARD_H = 40.0, 24.0+BX_MIN, BY_MIN = 130.0 - BOARD_W / 2, 110.0 - BOARD_H / 2+BX_MAX, BY_MAX = 130.0 + BOARD_W / 2, 110.0 + BOARD_H / 2+EDGE_OFFSET = 1.0    # mm — corner-pin offset per kicad-to-molecule medium rules+GRID = 8.0           # mm — Adom medium grid++# ── Helpers ─────────────────────────────────────────────────────────────+++def extract_symbol_def(lib_path: Path, sym_name: str,+                       qualify_with_lib: str | None = None) -> str:+    txt = lib_path.read_text(encoding="utf-8")+    needle = f'(symbol "{sym_name}"'+    i = txt.find(needle)+    if i < 0:+        raise RuntimeError(f"Symbol {sym_name!r} not found in {lib_path}")+    depth = 0+    end = i+    for j in range(i, len(txt)):+        if txt[j] == "(":+            depth += 1+        elif txt[j] == ")":+            depth -= 1+            if depth == 0:+                end = j + 1+                break+    block = txt[i:end]+    if qualify_with_lib:+        block = block.replace(needle, f'(symbol "{qualify_with_lib}:{sym_name}"', 1)+    return block+++def extract_footprint_body(fp_path: Path, instance_name: str) -> str:+    txt = fp_path.read_text(encoding="utf-8")+    txt = re.sub(+        r'^\(footprint "[^"]+"',+        f'(footprint "AdomRP2040:{instance_name}"',+        txt,+        count=1,+    )+    return txt+++def place_footprint(fp_body: str, x: float, y: float, rot: float = 0) -> str:+    """Inject (uuid …) + (at X Y rot) into a footprint body."""+    inst_uuid = str(uuid.uuid4())+    insertion = f'\n\t(uuid "{inst_uuid}")\n\t(at {x} {y}{(" " + str(rot)) if rot else ""})'+    lines = fp_body.splitlines()+    for i, line in enumerate(lines):+        if line.strip().startswith('(layer "F.Cu")'):+            lines.insert(i + 1, insertion.lstrip("\n"))+            break+    return "\n".join(lines)+++def model_block(model_ref: str | None) -> str:+    """Emit a (model …) block. `model_ref` is either a KiCad-std relative path+    like 'Capacitor_SMD.3dshapes/C_0402_1005Metric.step' (resolved via+    ${KICAD<n>_3DMODEL_DIR}) OR an in-pack path starting with `pack:`.+    Returns "" if model_ref is None."""+    if not model_ref:+        return ""+    if model_ref.startswith("pack:"):+        # Relative to this footprint .pretty/ → ../AdomRP2040.3dshapes/<name>+        rel = model_ref[len("pack:"):]+        full = f"${{KIPRJMOD}}/../AdomRP2040.3dshapes/{rel}"+    else:+        full = f"${{KICAD{KICAD_VERSION}_3DMODEL_DIR}}/{model_ref}"+    return (+        f'\n\t(model "{full}"\n'+        f'\t\t(offset (xyz 0 0 0))\n'+        f'\t\t(scale (xyz 1 1 1))\n'+        f'\t\t(rotate (xyz 0 0 0))\n'+        f'\t)'+    )+++def make_uuid() -> str:+    return str(uuid.uuid4())+++# ── BOM ─────────────────────────────────────────────────────────────────+#+# Each entry: (ref, value, sym_lib, sym_name, fp_lib, fp_name, model_ref,+#              sch_x, sch_y, sch_rot, pcb_x, pcb_y, pcb_rot)+#+# sch_*: schematic placement (free-form A4)+# pcb_*: board placement (mm, board-coords). No overlaps audited below.++# Compute perimeter machine-contact positions on the 8 mm grid.+# Adom convention: corner MPs at (offset, offset) etc.; intermediate MCs sit on+# the 8 mm grid between them. For a 40 × 24 board with offset 1 mm and grid 8,+# the available top-edge X positions are 119, 127, 135, 143 (4 contacts, all+# inside the 38 mm corner-to-corner span 111 → 149).+TOP_CONTACTS_X = [119.0, 127.0, 135.0, 143.0]+BOT_CONTACTS_X = [119.0, 127.0, 135.0, 143.0]++# Corner positions per the kicad-to-molecule convention (CCW from front-left).+CORNER_MP = {+    "MP1": (BX_MIN + EDGE_OFFSET, BY_MAX - EDGE_OFFSET),  # front-left  (111, 121)+    "MP2": (BX_MIN + EDGE_OFFSET, BY_MIN + EDGE_OFFSET),  # back-left   (111, 99)+    "MP3": (BX_MAX - EDGE_OFFSET, BY_MIN + EDGE_OFFSET),  # back-right  (149, 99)+    "MP4": (BX_MAX - EDGE_OFFSET, BY_MAX - EDGE_OFFSET),  # front-right (149, 121)+}++# ── Active component placement (chip + caps + crystal + USB-C) ─────────+# Coordinates chosen so no component bounding box overlaps another.+# QFN-56 body 7×7 mm (pads ~9.5×9.5 mm).++ACTIVE_PARTS = [+    # (ref, value, sym_lib, sym_name, fp_lib, fp_name, model_ref,+    #  sch_x, sch_y, sch_rot, pcb_x, pcb_y, pcb_rot)+    # The RP2040 sits centre-left of the chip area to leave space for the+    # crystal on its right and the USB-C on the board's left edge.+    ("U1", "RP2040",+     "AdomRP2040", "RP2040_AdomTour",+     "AdomRP2040", "QFN-56_AdomRP2040",+     None,+     150, 100, 0,+     130, 110, 0),++    # 3× 100 nF / 1 µF decoupling caps, kept clear of the chip body (chip+    # body extends ~4.75 mm from centre in each direction including pads).+    # Place them in the gap between the chip and the top-edge contacts row.+    ("C1", "100nF",+     "Device", "C",+     "Capacitor_SMD", "C_0402_1005Metric",+     "Capacitor_SMD.3dshapes/C_0402_1005Metric.step",+     115, 65, 0,+     126.5, 102.5, 0),+    ("C2", "100nF",+     "Device", "C",+     "Capacitor_SMD", "C_0402_1005Metric",+     "Capacitor_SMD.3dshapes/C_0402_1005Metric.step",+     130, 65, 0,+     133.5, 102.5, 0),+    ("C3", "1uF",+     "Device", "C",+     "Capacitor_SMD", "C_0402_1005Metric",+     "Capacitor_SMD.3dshapes/C_0402_1005Metric.step",+     145, 65, 0,+     130, 117, 0),++    # 12 MHz crystal next to the chip (right side, clear of the QFN pads).+    # The 3225 crystal is 3.2 × 2.5 mm body; load caps tuck above/below.+    ("Y1", "12MHz",+     "Device", "Crystal",+     "Crystal", "Crystal_SMD_3225-4Pin_3.2x2.5mm",+     "Crystal.3dshapes/Crystal_SMD_3225-4Pin_3.2x2.5mm.step",+     180, 100, 0,+     140, 110, 0),+    ("C4", "22pF",+     "Device", "C",+     "Capacitor_SMD", "C_0402_1005Metric",+     "Capacitor_SMD.3dshapes/C_0402_1005Metric.step",+     180, 130, 0,+     140, 106, 0),+    ("C5", "22pF",+     "Device", "C",+     "Capacitor_SMD", "C_0402_1005Metric",+     "Capacitor_SMD.3dshapes/C_0402_1005Metric.step",+     180, 145, 0,+     140, 114, 0),++    # USB-C receptacle on the LEFT short edge — opening pointing OUTWARD+    # (west / -X) so a cable plugged in from the left actually mates.+    # v3.1 fix: rot=270 (not 90 — v3 had it backwards) and shifted west+    # so the opening sits flush with the board's left edge.+    #+    # Amphenol 12401610E4-2A footprint (rot=0) extents: X = [-5.39, 5.39],+    # Y = [-6.36, 6.14], pads-cluster at y=-5.02 (BACK of the connector).+    # The receptacle opening is at the +Y extreme. After rot=270, the+    # opening lies at -X relative to the placement origin — facing west,+    # which is what we want for cable insertion from outside the board.+    #+    # Placement: centre x=114 — the opening edge sits ~2 mm past the+    # board's left edge (at x=110) for cable clearance, while the+    # mounting tabs / SMT pads at centre.x + 5.02 ≈ 119 are pushed+    # deeper into the FR4 so the through-hole tabs root cleanly.+    ("J1", "USB-C",+     "Connector", "USB_C_Receptacle_USB2.0_16P",+     "Connector_USB", "USB_C_Receptacle_Amphenol_12401610E4-2A",+     "Connector_USB.3dshapes/USB_C_Receptacle_Amphenol_12401610E4-2A.step",+     90, 100, 0,+     114, 110, 270),+]++# Corner Adom machine pins (no signal). The ADOM_MECHANICAL_PIN symbol fits.+CORNER_PARTS = [+    (ref, "MP",+     "AdomRP2040", "ADOM_MECHANICAL_PIN",+     "AdomRP2040", "ADOM_MEDIUM_PIN_SHORTER_v1",+     "pack:MachinePin_Medium_Short.step",+     # Sch placement off to the right of the active sketch+     220 + (i % 2) * 12, 60 + (i // 2) * 14, 0,+     CORNER_MP[ref][0], CORNER_MP[ref][1], 0)+    for i, ref in enumerate(CORNER_MP)+]++# Perimeter Adom machine contacts (signal-carrying). Numbered MC1..MCn+# counterclockwise from front-left along the top edge first, then the+# right side (if any), then the bottom.+CONTACT_PARTS: list[tuple] = []+mc_idx = 1+# Top edge (y = BY_MIN + EDGE_OFFSET = 99)+for x in TOP_CONTACTS_X:+    ref = f"MC{mc_idx}"+    CONTACT_PARTS.append((+        ref, "MC",+        "AdomRP2040", "ADOM_SIGNAL_PIN",+        "AdomRP2040", "ADOM_MEDIUM_CONTACT",+        "pack:MediumMachineContact v1.step",+        45 + (mc_idx - 1) * 10, 170, 0,+        x, BY_MIN + EDGE_OFFSET, 0,+    ))+    mc_idx += 1+# Bottom edge (y = BY_MAX - EDGE_OFFSET = 121) — reverse so MCs flow CCW+for x in reversed(BOT_CONTACTS_X):+    ref = f"MC{mc_idx}"+    CONTACT_PARTS.append((+        ref, "MC",+        "AdomRP2040", "ADOM_SIGNAL_PIN",+        "AdomRP2040", "ADOM_MEDIUM_CONTACT",+        "pack:MediumMachineContact v1.step",+        45 + (mc_idx - 9) * 10, 195, 0,+        x, BY_MAX - EDGE_OFFSET, 0,+    ))+    mc_idx += 1++PARTS = ACTIVE_PARTS + CORNER_PARTS + CONTACT_PARTS+++# ── Overlap audit ───────────────────────────────────────────────────────+++def audit_overlaps() -> list[str]:+    """Conservative bounding-box overlap check on PCB placements."""+    # Approximate footprint extents (mm).  These are total pad+body extent+    # measured from the placement origin (which is usually the footprint centre).+    bbox = {+        "QFN-56_AdomRP2040": (5.5, 5.5),+        "C_0402_1005Metric": (1.5, 0.9),+        "Crystal_SMD_3225-4Pin_3.2x2.5mm": (2.0, 1.6),+        "USB_C_Receptacle_Amphenol_12401610E4-2A": (5.0, 5.5),+        "ADOM_MEDIUM_PIN_SHORTER_v1": (0.9, 0.9),+        "ADOM_MEDIUM_CONTACT": (0.7, 0.7),+    }+    issues = []+    placed = []+    for p in PARTS:+        (ref, _v, _sl, _sn, _fl, fp_name, _mref,+         _sx, _sy, _srot, px, py, prot) = p+        ext = bbox.get(fp_name, (1.0, 1.0))+        # Swap extents for 90/270 rotation+        if prot in (90, 270, -90):+            ext = (ext[1], ext[0])+        placed.append((ref, px, py, ext))+    for i in range(len(placed)):+        ra, ax, ay, (aw, ah) = placed[i]+        for j in range(i + 1, len(placed)):+            rb, bx, by, (bw, bh) = placed[j]+            if abs(ax - bx) < (aw + bw) and abs(ay - by) < (ah + bh):+                # Skip corner-MP vs contact (tiny overlap is OK between them)+                if ra.startswith("MP") and rb.startswith("MC"):+                    continue+                if ra.startswith("MC") and rb.startswith("MP"):+                    continue+                issues.append(f"{ra} ↔ {rb}: bbox overlap at ({px}={ax}/{ay}) ({by})")+    return issues+++# ── Symbol instance emitter ────────────────────────────────────────────+++def emit_symbol_instance(part: tuple, sch_uuid: str) -> str:+    (ref, value, sym_lib, sym_name, fp_lib, fp_name, _mref,+     sx, sy, srot, _px, _py, _prot) = part+    inst_uuid = make_uuid()+    pin_lines = "\n".join(+        f'\t\t(pin "{i+1}" (uuid "{make_uuid()}"))' for i in range(30)+    )+    fp_prop = f"{fp_lib}:{fp_name}"+    return f"""\t(symbol+\t\t(lib_id "{sym_lib}:{sym_name}")+\t\t(at {sx} {sy} {srot})+\t\t(unit 1)+\t\t(exclude_from_sim no)+\t\t(in_bom yes)+\t\t(on_board yes)+\t\t(dnp no)+\t\t(uuid "{inst_uuid}")+\t\t(property "Reference" "{ref}"+\t\t\t(at {sx + 2.54} {sy - 5} 0)+\t\t\t(effects (font (size 1.27 1.27)))+\t\t)+\t\t(property "Value" "{value}"+\t\t\t(at {sx + 2.54} {sy + 5} 0)+\t\t\t(effects (font (size 1.27 1.27)))+\t\t)+\t\t(property "Footprint" "{fp_prop}"+\t\t\t(at {sx} {sy} 0)+\t\t\t(effects (font (size 1.27 1.27)) (hide yes))+\t\t)+\t\t(property "Datasheet" "~"+\t\t\t(at {sx} {sy} 0)+\t\t\t(effects (font (size 1.27 1.27)) (hide yes))+\t\t)+\t\t(property "Description" "{value}"+\t\t\t(at {sx} {sy} 0)+\t\t\t(effects (font (size 1.27 1.27)) (hide yes))+\t\t)+{pin_lines}+\t\t(instances+\t\t\t(project "rp2040-breakout"+\t\t\t\t(path "/{sch_uuid}"+\t\t\t\t\t(reference "{ref}")+\t\t\t\t\t(unit 1)+\t\t\t\t)+\t\t\t)+\t\t)+\t)"""+++def sym_lib_path(sym_lib: str) -> Path:+    if sym_lib == "AdomRP2040":+        return HERE / "AdomRP2040.kicad_sym"+    return KICAD_ROOT / "symbols" / f"{sym_lib}.kicad_sym"+++def fp_lib_path(fp_lib: str, fp_name: str) -> Path:+    if fp_lib == "AdomRP2040":+        return HERE / "AdomRP2040.pretty" / f"{fp_name}.kicad_mod"+    return KICAD_ROOT / "footprints" / f"{fp_lib}.pretty" / f"{fp_name}.kicad_mod"+++def main() -> int:+    issues = audit_overlaps()+    if issues:+        print("WARN: overlap audit found issues — review before shipping:")+        for it in issues:+            print(f"  • {it}")+    else:+        print(f"overlap audit: clean ({len(PARTS)} components)")++    template_dir = HERE / "template"+    template_dir.mkdir(exist_ok=True)++    # ── lib_symbols block (unique symbols only) ────────────────────────+    seen = set()+    lib_symbol_blocks: list[str] = []+    for p in PARTS:+        key = (p[2], p[3])  # (sym_lib, sym_name)+        if key in seen:+            continue+        seen.add(key)+        lib_symbol_blocks.append(extract_symbol_def(+            sym_lib_path(p[2]), p[3], qualify_with_lib=p[2]))++    sch_uuid = make_uuid()+    sym_instances = "\n".join(emit_symbol_instance(p, sch_uuid) for p in PARTS)++    sch = f"""(kicad_sch+\t(version 20231120)+\t(generator "eeschema")+\t(generator_version "9.0")+\t(uuid "{sch_uuid}")+\t(paper "A4")+\t(title_block+\t\t(title "Adom RP2040 Breakout")+\t\t(date "2026-06-07")+\t\t(comment 1 "RP2040 molecule: 4 corner Adom MPs + 8 perimeter Adom MCs.")+\t\t(comment 2 "Decoupling, crystal, USB-C — placement only, no nets emitted.")+\t)+\t(lib_symbols+{chr(10).join(lib_symbol_blocks)}+\t)+{sym_instances}+\t(sheet_instances+\t\t(path "/"+\t\t\t(page "1")+\t\t)+\t)+\t(embedded_fonts no)+)+"""+    sch_path = template_dir / "rp2040-breakout.kicad_sch"+    sch_path.write_text(sch, encoding="utf-8")+    print(f"wrote {sch_path} ({len(sch):,} bytes)")++    # ── Board ──────────────────────────────────────────────────────────+    fp_blocks = []+    for p in PARTS:+        (_ref, _value, _sl, _sn, fp_lib, fp_name, model_ref,+         _sx, _sy, _srot, px, py, prot) = p+        body = extract_footprint_body(fp_lib_path(fp_lib, fp_name), fp_name)+        # If the source has no (model …) block, append one.+        if "(model " not in body and model_ref:+            body = body.rstrip()+            if body.endswith(")"):+                body = body[:-1] + model_block(model_ref) + "\n)\n"+        body = place_footprint(body, px, py, prot)+        fp_blocks.append(body)++    edge_uuids = [make_uuid() for _ in range(4)]+    pcb = f"""(kicad_pcb+\t(version 20240108)+\t(generator "pcbnew")+\t(generator_version "9.0")+\t(general (thickness 1.6) (legacy_teardrops no))+\t(paper "A4")+\t(layers+\t\t(0 "F.Cu" signal) (31 "B.Cu" signal)+\t\t(32 "B.Adhes" user "B.Adhesive") (33 "F.Adhes" user "F.Adhesive")+\t\t(34 "B.Paste" user) (35 "F.Paste" user)+\t\t(36 "B.SilkS" user "B.Silkscreen") (37 "F.SilkS" user "F.Silkscreen")+\t\t(38 "B.Mask" user "B.Mask") (39 "F.Mask" user "F.Mask")+\t\t(40 "Dwgs.User" user "User.Drawings") (41 "Cmts.User" user "User.Comments")+\t\t(42 "Eco1.User" user "User.Eco1") (43 "Eco2.User" user "User.Eco2")+\t\t(44 "Edge.Cuts" user) (45 "Margin" user)+\t\t(46 "B.CrtYd" user "B.Courtyard") (47 "F.CrtYd" user "F.Courtyard")+\t\t(48 "B.Fab" user) (49 "F.Fab" user)+\t)+\t(setup+\t\t(pad_to_mask_clearance 0)+\t\t(allow_soldermask_bridges_in_footprints no)+\t\t(tenting front back)+\t\t(pcbplotparams+\t\t\t(layerselection 0x00010fc_ffffffff)+\t\t\t(plot_on_all_layers_selection 0x0000000_00000000)+\t\t\t(disableapertmacros no) (usegerberextensions no)+\t\t\t(usegerberattributes yes) (usegerberadvancedattributes yes)+\t\t\t(creategerberjobfile yes)+\t\t\t(dashed_line_dash_ratio 12.000000) (dashed_line_gap_ratio 3.000000)+\t\t\t(svgprecision 4) (plotframeref no) (viasonmask no) (mode 1)+\t\t\t(useauxorigin no) (hpglpennumber 1) (hpglpenspeed 20)+\t\t\t(hpglpendiameter 15.000000)+\t\t\t(pdf_front_fp_property_popups yes) (pdf_back_fp_property_popups yes)+\t\t\t(pdf_metadata yes) (pdf_single_document no)+\t\t\t(dxfpolygonmode yes) (dxfimperialunits yes) (dxfusepcbnewfont yes)+\t\t\t(psnegative no) (psa4output no) (plot_black_and_white yes)+\t\t\t(plotinvisibletext no) (sketchpadsonfab no) (plotpadnumbers no)+\t\t\t(hidednponfab no) (sketchdnponfab yes) (crossoutdnponfab yes)+\t\t\t(subtractmaskfromsilk no) (outputformat 1) (mirror no)+\t\t\t(drillshape 1) (scaleselection 1)+\t\t\t(outputdirectory "gerbers/")+\t\t)+\t)+\t(net 0 "")+{chr(10).join(fp_blocks)}+\t(gr_line (start {BX_MIN} {BY_MIN}) (end {BX_MAX} {BY_MIN})+\t\t(stroke (width 0.1) (type default)) (layer "Edge.Cuts") (uuid "{edge_uuids[0]}"))+\t(gr_line (start {BX_MAX} {BY_MIN}) (end {BX_MAX} {BY_MAX})+\t\t(stroke (width 0.1) (type default)) (layer "Edge.Cuts") (uuid "{edge_uuids[1]}"))+\t(gr_line (start {BX_MAX} {BY_MAX}) (end {BX_MIN} {BY_MAX})+\t\t(stroke (width 0.1) (type default)) (layer "Edge.Cuts") (uuid "{edge_uuids[2]}"))+\t(gr_line (start {BX_MIN} {BY_MAX}) (end {BX_MIN} {BY_MIN})+\t\t(stroke (width 0.1) (type default)) (layer "Edge.Cuts") (uuid "{edge_uuids[3]}"))+)+"""+    pcb_path = template_dir / "rp2040-breakout.kicad_pcb"+    pcb_path.write_text(pcb, encoding="utf-8")+    print(f"wrote {pcb_path} ({len(pcb):,} bytes)")++    pro = (+        '{\n'+        '  "board": {"design_settings": {"defaults": {}, "rule_severities": {}, "rules": {}}, "layer_presets": [], "viewports": []},\n'+        '  "boards": [],\n'+        '  "cvpcb": {"equivalence_files": []},\n'+        '  "libraries": {"pinned_footprint_libs": ["AdomRP2040"], "pinned_symbol_libs": ["AdomRP2040"]},\n'+        '  "meta": {"filename": "rp2040-breakout.kicad_pro", "version": 3},\n'+        '  "pcbnew": {"last_paths": {"plot": "gerbers/"}, "page_layout_descr_file": ""},\n'+        '  "schematic": {"annotate_messages": "", "legacy_lib_dir": "", "legacy_lib_list": []},\n'+        '  "sheets": [], "text_variables": {}\n'+        '}\n'+    )+    pro_path = template_dir / "rp2040-breakout.kicad_pro"+    pro_path.write_text(pro, encoding="utf-8")+    print(f"wrote {pro_path}")+    return 0+++if __name__ == "__main__":+    sys.exit(main())
tour-pack/AdomTour.kicad_symadded+129
@@ -0,0 +1,129 @@+(kicad_symbol_lib+	(version 20231120)+	(generator "kicad_symbol_editor")+	(generator_version "9.0")+	(symbol "R_AdomTour"+		(pin_numbers+			(hide yes)+		)+		(pin_names+			(offset 0)+		)+		(exclude_from_sim no)+		(in_bom yes)+		(on_board yes)+		(property "Reference" "R"+			(at 2.032 0 90)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "Value" "R_AdomTour"+			(at 0 0 90)+			(effects+				(font+					(size 1.27 1.27)+				)+			)+		)+		(property "Footprint" "AdomTour:R_0805_AdomTour"+			(at -1.778 0 90)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "Datasheet" "~"+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "Description" "Adom Tour demo resistor"+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "ki_keywords" "R res resistor adom tour"+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(property "ki_fp_filters" "R_*"+			(at 0 0 0)+			(effects+				(font+					(size 1.27 1.27)+				)+				(hide yes)+			)+		)+		(symbol "R_AdomTour_0_1"+			(rectangle+				(start -1.016 -2.54)+				(end 1.016 2.54)+				(stroke+					(width 0.254)+					(type default)+				)+				(fill+					(type none)+				)+			)+		)+		(symbol "R_AdomTour_1_1"+			(pin passive line+				(at 0 3.81 270)+				(length 1.27)+				(name "~"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "1"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+			(pin passive line+				(at 0 -3.81 90)+				(length 1.27)+				(name "~"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+				(number "2"+					(effects+						(font+							(size 1.27 1.27)+						)+					)+				)+			)+		)+		(embedded_fonts no)+	)+)
tour-pack/AdomTour.pretty/R_0805_AdomTour.kicad_modadded+112
@@ -0,0 +1,112 @@+(footprint "R_0805_AdomTour"+	(version 20260206)+	(generator "kicad-footprint-generator")+	(layer "F.Cu")+	(descr "Resistor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC-7351 nominal, (Body size source: IPC-SM-782 page 72, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf)")+	(tags "resistor")+	(property "Reference" "REF**"+		(at 0 -1.65 0)+		(layer "F.SilkS")+		(effects+			(font+				(size 1 1)+				(thickness 0.15)+			)+		)+	)+	(property "Value" "R_0805_2012Metric"+		(at 0 1.65 0)+		(layer "F.Fab")+		(effects+			(font+				(size 1 1)+				(thickness 0.15)+			)+		)+	)+	(property "KiLib_Generator" "SMD_2terminal_chip_molded"+		(at 0 0 0)+		(layer "F.SilkS")+		(hide yes)+		(effects+			(font+				(size 1 1)+				(thickness 0.15)+			)+		)+	)+	(attr smd)+	(duplicate_pad_numbers_are_jumpers no)+	(fp_line+		(start -0.227064 -0.735)+		(end 0.227064 -0.735)+		(stroke+			(width 0.12)+			(type solid)+		)+		(layer "F.SilkS")+	)+	(fp_line+		(start -0.227064 0.735)+		(end 0.227064 0.735)+		(stroke+			(width 0.12)+			(type solid)+		)+		(layer "F.SilkS")+	)+	(fp_rect+		(start -1.68 -0.95)+		(end 1.68 0.95)+		(stroke+			(width 0.05)+			(type solid)+		)+		(fill no)+		(layer "F.CrtYd")+	)+	(fp_rect+		(start -1 -0.625)+		(end 1 0.625)+		(stroke+			(width 0.1)+			(type solid)+		)+		(fill no)+		(layer "F.Fab")+	)+	(fp_text user "${REFERENCE}"+		(at 0 0 0)+		(layer "F.Fab")+		(effects+			(font+				(size 0.5 0.5)+				(thickness 0.08)+			)+		)+	)+	(pad "1" smd roundrect+		(at -0.9125 0)+		(size 1.025 1.4)+		(layers "F.Cu" "F.Mask" "F.Paste")+		(roundrect_rratio 0.243902)+	)+	(pad "2" smd roundrect+		(at 0.9125 0)+		(size 1.025 1.4)+		(layers "F.Cu" "F.Mask" "F.Paste")+		(roundrect_rratio 0.243902)+	)+	(embedded_fonts no)+	(model "${KICAD10_3DMODEL_DIR}/Resistor_SMD.3dshapes/R_0805_2012Metric.step"+		(offset+			(xyz 0 0 0)+		)+		(scale+			(xyz 1 1 1)+		)+		(rotate+			(xyz 0 0 0)+		)+	)+)
tour/__init__.pyadded+1
@@ -0,0 +1 @@+"""KiCad Feature Tour — self-running companion app."""
tour/bridge_client.pyadded+32
@@ -0,0 +1,32 @@+"""HTTP client for the KiCad bridge server (port 8772)."""++import json+import urllib.request++BRIDGE_URL = "http://127.0.0.1:8772"+++def send_command(command: str, args: dict | None = None, timeout: float = 30.0) -> dict:+    """Send a command to the KiCad bridge server. Returns the JSON response."""+    payload = json.dumps({"command": command, "args": args or {}}).encode("utf-8")+    req = urllib.request.Request(+        f"{BRIDGE_URL}/command",+        data=payload,+        headers={"Content-Type": "application/json"},+        method="POST",+    )+    try:+        with urllib.request.urlopen(req, timeout=timeout) as resp:+            return json.loads(resp.read().decode("utf-8"))+    except Exception as e:+        return {"success": False, "error": str(e)}+++def health_check() -> dict | None:+    """Check if the bridge server is running. Returns health dict or None."""+    try:+        req = urllib.request.Request(f"{BRIDGE_URL}/health")+        with urllib.request.urlopen(req, timeout=5) as resp:+            return json.loads(resp.read().decode("utf-8"))+    except Exception:+        return None
tour/highlight.pyadded+182
@@ -0,0 +1,182 @@+"""Transparent overlay to highlight a screen region. Win32 only.++Usage:+    python highlight.py <x> <y> <width> <height> [options]++Options:+    --color HEX      Border color (default: #00b8b1 — Adom teal)+    --duration MS     Auto-dismiss after N milliseconds (default: 5000)+    --label TEXT      Text badge above the highlight box+    --thickness N     Border thickness in pixels (default: 3)++Coordinates are in PHYSICAL pixels (matching DPI-aware screenshots).+On high-DPI displays (e.g., 150%), tkinter coordinates are logical, so+this tool scales from physical -> logical automatically.++The overlay is always-on-top and auto-dismisses after --duration ms.+Zero pip deps — uses only tkinter + ctypes.+"""++import argparse+import ctypes+import sys+import tkinter as tk++# Nearly-black color that Windows treats as transparent+_TRANSPARENT = "#010101"+++def _get_dpi_scale():+    """Get the DPI scale factor using Win32 GetDeviceCaps.++    Compares physical screen resolution (from GetDeviceCaps) with the+    logical resolution (from GetSystemMetrics). Works WITHOUT calling+    SetProcessDPIAware, so the process stays DPI-unaware and Windows+    handles all rendering scaling for us.+    """+    try:+        user32 = ctypes.windll.user32+        gdi32 = ctypes.windll.gdi32++        # Logical screen size (what a DPI-unaware process sees)+        logical_w = user32.GetSystemMetrics(0)++        # Physical screen size (actual pixel count)+        hdc = user32.GetDC(0)+        physical_w = gdi32.GetDeviceCaps(hdc, 118)  # DESKTOPHORZRES+        user32.ReleaseDC(0, hdc)++        if logical_w > 0 and physical_w > 0:+            return physical_w / logical_w+        return 1.0+    except Exception:+        return 1.0+++# NOTE: We intentionally do NOT call SetProcessDPIAware here.+# Tkinter works best as a DPI-unaware process: we convert physical+# input coords to logical, and Windows handles the rest.+++def show_highlight(x, y, width, height, color="#00b8b1", duration=5000,+                   label="", thickness=3):+    """Show a colored border overlay at the given screen coordinates.++    Coordinates are in PHYSICAL pixels (matching DPI-aware screenshots).+    Internally converts to logical (DPI-unaware) coordinates for tkinter.+    """++    # Convert physical pixels -> logical pixels for DPI-unaware tkinter+    scale = _get_dpi_scale()+    x = int(x / scale)+    y = int(y / scale)+    width = int(width / scale)+    height = int(height / scale)++    pad = thickness + 2+    label_h = 26 if label else 0++    total_w = width + pad * 2+    total_h = height + pad * 2 + label_h++    root = tk.Tk()+    root.title("")+    root.overrideredirect(True)+    root.attributes("-topmost", True)+    root.attributes("-transparentcolor", _TRANSPARENT)+    root.configure(bg=_TRANSPARENT)+    root.geometry(f"{total_w}x{total_h}+{x - pad}+{y - pad - label_h}")++    canvas = tk.Canvas(+        root, bg=_TRANSPARENT, highlightthickness=0,+        width=total_w, height=total_h,+    )+    canvas.pack()++    # -- Main border rectangle+    ry = label_h+    canvas.create_rectangle(+        pad, ry + pad, width + pad, ry + height + pad,+        outline=color, width=thickness, tags="border",+    )++    # -- Corner accents (L-shaped marks for extra visibility)+    clen = max(12, min(24, width // 6, height // 6))+    thick2 = thickness + 2+    corners = [+        (pad, ry + pad, 1, 1),                      # top-left+        (width + pad, ry + pad, -1, 1),              # top-right+        (pad, ry + height + pad, 1, -1),             # bottom-left+        (width + pad, ry + height + pad, -1, -1),    # bottom-right+    ]+    for cx, cy, dx, dy in corners:+        canvas.create_line(cx, cy, cx + dx * clen, cy,+                           fill=color, width=thick2)+        canvas.create_line(cx, cy, cx, cy + dy * clen,+                           fill=color, width=thick2)++    # -- Label badge+    if label:+        badge_w = min(total_w, len(label) * 9 + 24)+        canvas.create_rectangle(0, 0, badge_w, label_h,+                                fill=color, outline="")+        canvas.create_text(+            badge_w // 2, label_h // 2, text=label,+            fill="white", font=("Segoe UI", 10, "bold"),+        )++    # -- Gentle pulse animation+    bright = _lighten(color, 0.35)+    pulse_state = [0]++    def pulse():+        if pulse_state[0] >= 6:+            return+        c = bright if pulse_state[0] % 2 == 0 else color+        canvas.itemconfig("border", outline=c)+        pulse_state[0] += 1+        root.after(350, pulse)++    root.after(200, pulse)++    # -- Auto-dismiss+    root.after(duration, root.destroy)+    root.mainloop()+++def _lighten(hex_color, factor):+    """Lighten a hex color by factor (0-1)."""+    h = hex_color.lstrip("#")+    r, g, b = int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)+    r = min(255, int(r + (255 - r) * factor))+    g = min(255, int(g + (255 - g) * factor))+    b = min(255, int(b + (255 - b) * factor))+    return f"#{r:02x}{g:02x}{b:02x}"+++def main():+    parser = argparse.ArgumentParser(+        description="Show highlight overlay on screen")+    parser.add_argument("x", type=int, help="X position (pixels from left)")+    parser.add_argument("y", type=int, help="Y position (pixels from top)")+    parser.add_argument("width", type=int, help="Width of highlight region")+    parser.add_argument("height", type=int, help="Height of highlight region")+    parser.add_argument("--color", default="#00b8b1",+                        help="Border color (default: Adom teal)")+    parser.add_argument("--duration", type=int, default=5000,+                        help="Auto-dismiss after N ms (default: 5000)")+    parser.add_argument("--label", default="",+                        help="Text badge above the highlight")+    parser.add_argument("--thickness", type=int, default=3,+                        help="Border thickness in px (default: 3)")+    args = parser.parse_args()++    show_highlight(+        args.x, args.y, args.width, args.height,+        color=args.color, duration=args.duration,+        label=args.label, thickness=args.thickness,+    )+++if __name__ == "__main__":+    main()
tour/ipc_client.pyadded+94
@@ -0,0 +1,94 @@+"""Subprocess wrappers for KiCad IPC Rust binaries."""++import os+import re+import subprocess+from pathlib import Path++# Paths to IPC binaries (built from Rust examples)+_EXAMPLES_DIR = (+    Path(__file__).resolve().parents[3]+    / "desktop"+    / "src-tauri"+    / "target"+    / "release"+    / "examples"+)+_TEST_BINARY = _EXAMPLES_DIR / "test_kicad_ipc.exe"+_LAYER_BINARY = _EXAMPLES_DIR / "ipc_set_layer.exe"+++def ensure_dummy_socket():+    """Create the dummy socket file for the Windows NNG workaround."""+    temp = os.environ.get("TEMP", os.environ.get("TMP", ""))+    if temp:+        sock_dir = Path(temp) / "kicad"+        sock_dir.mkdir(exist_ok=True)+        sock_file = sock_dir / "api.sock"+        if not sock_file.exists():+            sock_file.touch()+++def has_test_binary() -> bool:+    return _TEST_BINARY.exists()+++def has_layer_binary() -> bool:+    return _LAYER_BINARY.exists()+++def run_ipc_test() -> dict:+    """Run the full IPC test binary and parse sectioned output.++    Returns dict keyed by test number (int) with the text for that section.+    """+    if not has_test_binary():+        return {}+    ensure_dummy_socket()+    try:+        result = subprocess.run(+            [str(_TEST_BINARY)],+            capture_output=True,+            text=True,+            timeout=30,+        )+        return _parse_test_output(result.stdout)+    except Exception as e:+        return {0: f"IPC test failed: {e}"}+++def set_active_layer(layer_id: int) -> dict:+    """Set KiCad's active layer via IPC."""+    if not has_layer_binary():+        return {"success": False, "output": "ipc_set_layer.exe not found"}+    ensure_dummy_socket()+    try:+        result = subprocess.run(+            [str(_LAYER_BINARY), str(layer_id)],+            capture_output=True,+            text=True,+            timeout=10,+        )+        output = result.stdout.strip() or result.stderr.strip()+        return {"success": result.returncode == 0, "output": output}+    except Exception as e:+        return {"success": False, "output": str(e)}+++def _parse_test_output(stdout: str) -> dict:+    """Parse test_kicad_ipc.exe output into sections by [N] headers."""+    sections = {}+    current_key = None+    current_lines = []+    for line in stdout.splitlines():+        match = re.match(r"\[(\d+)\]", line)+        if match:+            if current_key is not None:+                sections[current_key] = "\n".join(current_lines)+            current_key = int(match.group(1))+            current_lines = [line]+        elif current_key is not None:+            current_lines.append(line)+    if current_key is not None:+        sections[current_key] = "\n".join(current_lines)+    return sections
tour/narration.pyadded+348
@@ -0,0 +1,348 @@+"""Narration panel for the KiCad tour.++Styled to match the Adom Desktop Tauri app exactly:+  Colors: desktop/src/style.css CSS custom properties+  Fonts:  Segoe UI (body) + Cascadia Code (mono)+  Layout: bg-primary page, bg-secondary panels, bg-tertiary hover++Runs standalone with an HTTP server so Claude can push updates:+    python narration.py [--port 8799]++HTTP API:+    POST /update   {"step":1,"total":19,"title":"...","description":"...",+                     "act":"...","status":"...","status_color":"#3fb950",+                     "data":"...","data_color":"#3fb950"}+    POST /quit     Close the panel+    GET  /health   {"status":"ok"}+"""++import argparse+import ctypes+import json+import sys+import threading+import tkinter as tk+from http.server import HTTPServer, BaseHTTPRequestHandler+from tkinter import ttk+++# ── Colors — exact match to desktop/src/style.css :root vars ─────+BG_PRIMARY = "#0d1117"       # --bg-primary  (page background)+BG_SECONDARY = "#161b22"     # --bg-secondary (panels, toolbars)+BG_TERTIARY = "#21262d"      # --bg-tertiary  (hover, elevated)+BORDER = "#30363d"           # --border+TEXT_PRIMARY = "#e6edf3"     # --text-primary+TEXT_SECONDARY = "#8b949e"   # --text-secondary+TEXT_MUTED = "#6e7681"       # --text-muted+ACCENT = "#58a6ff"           # --accent+GREEN = "#3fb950"            # --green+RED = "#f85149"              # --red+YELLOW = "#d29922"           # --yellow+ORANGE = "#db6d28"           # --orange++# Brand accent+TEAL = "#00b8b1"+TEAL_BRIGHT = "#00e6dc"+PURPLE = "#8C6BF7"+PURPLE_LIGHT = "#C5B3FF"+BLUE = ACCENT+BLUE_LIGHT = "#64ABFF"++# Backward-compat aliases+FG = TEXT_PRIMARY+FG_DIM = TEXT_MUTED+FG_SECONDARY = TEXT_SECONDARY++# ── Typography ──────────────────────────────────────────────────+FONT_TITLE = ("Segoe UI", 18, "bold")+FONT_H2 = ("Segoe UI", 15, "bold")+FONT_ACT = ("Segoe UI", 11, "bold")+FONT_DESC = ("Segoe UI", 13)+FONT_MONO = ("Cascadia Code", 12)+FONT_STATUS = ("Segoe UI", 13)+FONT_BTN = ("Segoe UI", 13)+FONT_SMALL = ("Segoe UI", 12)+FONT_TAG = ("Segoe UI", 11, "bold")+++class NarrationPanel:+    """Always-on-top narration panel — styled identical to Adom Desktop."""++    def __init__(self, width: int = 480, on_quit=None):+        self.width = width+        self._on_quit = on_quit++        self.root = tk.Tk()+        self.root.title("Gallia Tour")+        self.root.configure(bg=BG_PRIMARY)+        self.root.attributes("-topmost", True)++        # Position on the right side of the screen, accounting for taskbar.+        # Use tkinter's own screen dimensions (logical pixels) so geometry+        # values are consistent regardless of DPI awareness.+        screen_w = self.root.winfo_screenwidth()+        screen_h = self.root.winfo_screenheight()+        # Subtract space for the taskbar (~50 logical px) and titlebar (~32 px)+        # to guarantee the Quit button is fully visible.+        panel_h = screen_h - 80+        x = screen_w - width+        self.root.geometry(f"{width}x{panel_h}+{x}+0")+        self.root.resizable(False, False)++        self.root.protocol("WM_DELETE_WINDOW", self._quit)++        self._build_ui()++        # Apply dark mode to the native Windows titlebar via DWM API.+        # This gives us a dark titlebar that matches the app without+        # removing it (which caused the invisible-window bug).+        self.root.update_idletasks()+        self._apply_dark_titlebar()++    def _apply_dark_titlebar(self):+        """Use DWM API to render the native titlebar in dark mode (Win10 1809+)."""+        try:+            hwnd = ctypes.windll.user32.GetParent(self.root.winfo_id())+            # DWMWA_USE_IMMERSIVE_DARK_MODE = 20 (Windows 10 1809+)+            DWMWA_USE_IMMERSIVE_DARK_MODE = 20+            value = ctypes.c_int(1)  # TRUE = dark mode+            ctypes.windll.dwmapi.DwmSetWindowAttribute(+                hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE,+                ctypes.byref(value), ctypes.sizeof(value),+            )+        except Exception:+            pass  # Fall back to default titlebar if DWM not available++    def _build_ui(self):+        # ── Status bar (matches #top-bar status group) ───+        status_bar = tk.Frame(self.root, bg=BG_SECONDARY, padx=16, pady=8)+        status_bar.pack(fill="x")++        # Green dot + step counter (like the Connected indicator)+        dot_canvas = tk.Canvas(+            status_bar, width=14, height=14,+            bg=BG_SECONDARY, highlightthickness=0,+        )+        dot_canvas.pack(side="left", padx=(0, 8))+        self._dot = dot_canvas+        self._dot_id = dot_canvas.create_oval(2, 2, 12, 12, fill=GREEN, outline="")++        self._step_label = tk.Label(+            status_bar, text="Gallia Feature Tour", font=FONT_SMALL,+            bg=BG_SECONDARY, fg=TEXT_SECONDARY,+        )+        self._step_label.pack(side="left")++        self._act_tag = tk.Label(+            status_bar, text="", font=FONT_TAG,+            bg="#1a1a33", fg="#a78bfa",+            padx=6, pady=1,+        )+        self._act_tag.pack(side="right")++        # Border+        tk.Frame(self.root, bg=BORDER, height=1).pack(fill="x")++        # ── Progress bar ─────────────────────────────+        prog_frame = tk.Frame(self.root, bg=BG_PRIMARY, padx=16)+        prog_frame.pack(fill="x", pady=(8, 0))++        style = ttk.Style()+        style.theme_use("default")+        style.configure(+            "Tour.Horizontal.TProgressbar",+            troughcolor=BG_TERTIARY,+            background=TEAL,+            thickness=4,+        )+        self._progress = ttk.Progressbar(+            prog_frame, style="Tour.Horizontal.TProgressbar",+            length=self.width - 32, mode="determinate",+        )+        self._progress.pack(fill="x")++        # ── Content area ─────────────────────────────+        content_frame = tk.Frame(self.root, bg=BG_PRIMARY, padx=16, pady=12)+        content_frame.pack(fill="x")++        self._section_label = tk.Label(+            content_frame, text="FEATURE TOUR", font=FONT_H2,+            bg=BG_PRIMARY, fg=TEXT_SECONDARY,+        )+        self._section_label.pack(anchor="w")++        self._title_label = tk.Label(+            content_frame, text="Waiting for Claude...", font=FONT_TITLE,+            bg=BG_PRIMARY, fg=TEXT_PRIMARY, anchor="w",+            wraplength=self.width - 32, justify="left",+        )+        self._title_label.pack(fill="x", pady=(8, 0))++        tk.Frame(self.root, bg=BORDER, height=1).pack(fill="x", padx=16, pady=8)++        self._desc_label = tk.Label(+            self.root, text="The tour will begin shortly.\nClaude is analyzing your setup...",+            font=FONT_DESC,+            bg=BG_PRIMARY, fg=TEXT_SECONDARY, padx=16, anchor="nw",+            wraplength=self.width - 32, justify="left",+        )+        self._desc_label.pack(fill="x")++        # ── Status line ──────────────────────────────+        self._status_label = tk.Label(+            self.root, text="", font=FONT_STATUS,+            bg=BG_PRIMARY, fg=GREEN, padx=16, anchor="w",+        )+        self._status_label.pack(fill="x", pady=(8, 0))++        # ── Data area (matches #activity-log) ────────+        data_outer = tk.Frame(self.root, bg=BG_PRIMARY, padx=16, pady=8)+        data_outer.pack(fill="both", expand=True)++        data_border = tk.Frame(data_outer, bg=BORDER, padx=1, pady=1)+        data_border.pack(fill="both", expand=True)++        self._data_text = tk.Text(+            data_border, font=FONT_MONO,+            bg=BG_SECONDARY, fg=TEXT_PRIMARY,+            insertbackground=GREEN,+            selectbackground=BG_TERTIARY,+            relief="flat", wrap="word",+            padx=8, pady=8,+            borderwidth=0, highlightthickness=0,+        )+        self._data_text.pack(fill="both", expand=True)+        self._data_text.config(state="disabled")++        # ── Button bar (matches #stats-footer) ───────+        tk.Frame(self.root, bg=BORDER, height=1).pack(fill="x", side="bottom")++        btn_frame = tk.Frame(self.root, bg=BG_SECONDARY, pady=8, padx=16)+        btn_frame.pack(fill="x", side="bottom")++        tk.Button(+            btn_frame, text="Quit Tour", command=self._quit,+            font=FONT_BTN, bg="#da3633", fg="#ffffff",+            activebackground="#b62324", activeforeground="#ffffff",+            relief="flat", padx=14, pady=6, cursor="hand2",+            borderwidth=1, highlightthickness=0,+        ).pack(side="right")++        # "Powered by Claude" label+        tk.Label(+            btn_frame, text="Powered by Claude",+            font=FONT_SMALL, bg=BG_SECONDARY, fg=TEXT_MUTED,+        ).pack(side="left")++    # ── Public API (thread-safe via root.after) ───────────────────++    def set_step(self, number: int, total: int, title: str, description: str,+                 act: str = ""):+        self._step_label.config(text=f"Step {number} of {total}")+        self._progress["maximum"] = total+        self._progress["value"] = number+        if act:+            self._act_tag.config(text=act.upper())+        self._title_label.config(text=title)+        self._desc_label.config(text=description)+        self._status_label.config(text="")+        self._data_text.config(state="normal")+        self._data_text.delete("1.0", "end")+        self._data_text.config(state="disabled")++    def set_status(self, text: str, color: str = GREEN):+        self._status_label.config(text=text, fg=color)++    def set_data(self, text: str):+        self._data_text.config(state="normal")+        self._data_text.delete("1.0", "end")+        self._data_text.insert("1.0", text)+        self._data_text.config(state="disabled")++    def set_data_color(self, color: str):+        self._data_text.config(fg=color)++    def apply_update(self, data: dict):+        """Apply a dict of updates from the HTTP API."""+        if "step" in data and "total" in data:+            self.set_step(+                data["step"], data["total"],+                data.get("title", ""), data.get("description", ""),+                data.get("act", ""),+            )+        else:+            if "title" in data:+                self._title_label.config(text=data["title"])+            if "description" in data:+                self._desc_label.config(text=data["description"])+            if "act" in data:+                self._act_tag.config(text=data["act"].upper())+        if "status" in data:+            color = data.get("status_color", GREEN)+            self.set_status(data["status"], color)+        if "data" in data:+            self.set_data(data["data"])+        if "data_color" in data:+            self.set_data_color(data["data_color"])++    # ── HTTP server for Claude to push updates ────────────────────++    def start_server(self, port: int = 8799):+        """Start the HTTP control server on a background thread."""+        panel = self++        class Handler(BaseHTTPRequestHandler):+            def do_POST(self):+                if self.path == "/update":+                    length = int(self.headers.get("Content-Length", 0))+                    body = json.loads(self.rfile.read(length)) if length else {}+                    panel.root.after(0, lambda: panel.apply_update(body))+                    self._respond(200, {"ok": True})+                elif self.path == "/quit":+                    self._respond(200, {"ok": True})+                    panel.root.after(100, panel.root.destroy)+                else:+                    self._respond(404, {"error": "not found"})++            def do_GET(self):+                if self.path == "/health":+                    self._respond(200, {"status": "ok", "port": port})+                else:+                    self._respond(404, {"error": "not found"})++            def _respond(self, code, data):+                self.send_response(code)+                self.send_header("Content-Type", "application/json")+                self.end_headers()+                self.wfile.write(json.dumps(data).encode())++            def log_message(self, fmt, *args):+                pass  # Suppress request logging++        server = HTTPServer(("127.0.0.1", port), Handler)+        t = threading.Thread(target=server.serve_forever, daemon=True)+        t.start()+        return server++    # ── Internal ──────────────────────────────────────────────────++    def _quit(self):+        if self._on_quit:+            self._on_quit()+        self.root.destroy()+++# ── Standalone mode ──────────────────────────────────────────────++if __name__ == "__main__":+    parser = argparse.ArgumentParser(description="Gallia Tour narration panel")+    parser.add_argument("--port", type=int, default=8799,+                        help="HTTP server port (default: 8799)")+    parser.add_argument("--width", type=int, default=480,+                        help="Panel width in pixels (default: 480)")+    args = parser.parse_args()++    panel = NarrationPanel(width=args.width)+    panel.start_server(port=args.port)+    print(f"Panel server listening on http://127.0.0.1:{args.port}")+    panel.root.mainloop()
tour/screenshot.pyadded+91
@@ -0,0 +1,91 @@+"""Capture desktop screenshots for tour debugging. Zero pip deps — Win32 only."""+import ctypes+import ctypes.wintypes+import struct+import sys+import zlib++user32 = ctypes.windll.user32+gdi32 = ctypes.windll.gdi32++# Make this process DPI-aware so we capture at the real physical resolution.+# Without this, topmost/overlay windows may not appear in the capture on+# high-DPI (125%/150%) displays.+try:+    user32.SetProcessDPIAware()+except Exception:+    pass+++def capture_screen(path: str):+    """Capture the entire screen to a PNG file."""+    w = user32.GetSystemMetrics(0)  # SM_CXSCREEN (physical after DPI-aware)+    h = user32.GetSystemMetrics(1)  # SM_CYSCREEN++    hdc_screen = user32.GetDC(0)+    hdc_mem = gdi32.CreateCompatibleDC(hdc_screen)+    hbmp = gdi32.CreateCompatibleBitmap(hdc_screen, w, h)+    gdi32.SelectObject(hdc_mem, hbmp)+    gdi32.BitBlt(hdc_mem, 0, 0, w, h, hdc_screen, 0, 0, 0x00CC0020)  # SRCCOPY++    # Read bitmap bits+    class BITMAPINFOHEADER(ctypes.Structure):+        _fields_ = [+            ("biSize", ctypes.c_uint32), ("biWidth", ctypes.c_int32),+            ("biHeight", ctypes.c_int32), ("biPlanes", ctypes.c_uint16),+            ("biBitCount", ctypes.c_uint16), ("biCompression", ctypes.c_uint32),+            ("biSizeImage", ctypes.c_uint32), ("biXPelsPerMeter", ctypes.c_int32),+            ("biYPelsPerMeter", ctypes.c_int32), ("biClrUsed", ctypes.c_uint32),+            ("biClrImportant", ctypes.c_uint32),+        ]++    bmi = BITMAPINFOHEADER()+    bmi.biSize = ctypes.sizeof(BITMAPINFOHEADER)+    bmi.biWidth = w+    bmi.biHeight = -h  # top-down+    bmi.biPlanes = 1+    bmi.biBitCount = 32+    bmi.biCompression = 0  # BI_RGB++    buf_size = w * h * 4+    buf = ctypes.create_string_buffer(buf_size)+    gdi32.GetDIBits(hdc_mem, hbmp, 0, h, buf, ctypes.byref(bmi), 0)++    # Clean up GDI+    gdi32.DeleteObject(hbmp)+    gdi32.DeleteDC(hdc_mem)+    user32.ReleaseDC(0, hdc_screen)++    # Convert BGRA -> RGB and write PNG manually+    raw = buf.raw+    _write_png(path, w, h, raw)+    print(f"Screenshot saved: {path} ({w}x{h})")++def _write_png(path, w, h, bgra_data):+    """Write a minimal PNG from BGRA pixel data."""+    # Convert BGRA rows to RGB with filter byte+    rows = []+    for y in range(h):+        row = bytearray([0])  # filter: None+        offset = y * w * 4+        for x in range(w):+            px = offset + x * 4+            b, g, r = bgra_data[px], bgra_data[px+1], bgra_data[px+2]+            row.extend([r, g, b])+        rows.append(bytes(row))+    raw_data = b"".join(rows)+    compressed = zlib.compress(raw_data, 6)++    def _chunk(ctype, data):+        c = ctype + data+        return struct.pack(">I", len(data)) + c + struct.pack(">I", zlib.crc32(c) & 0xFFFFFFFF)++    with open(path, "wb") as f:+        f.write(b"\x89PNG\r\n\x1a\n")+        f.write(_chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0)))+        f.write(_chunk(b"IDAT", compressed))+        f.write(_chunk(b"IEND", b""))++if __name__ == "__main__":+    out = sys.argv[1] if len(sys.argv) > 1 else "screenshot.png"+    capture_screen(out)
tour/steps.pyadded+326
@@ -0,0 +1,326 @@+"""Tour step definitions — every step shows something visual in KiCad."""++import os++# Action types — each one triggers a visible change on screen+DISPLAY = "display"           # Show text in panel only (intro/outro)+BRIDGE = "bridge"             # Bridge command + show data+BRIDGE_VISUAL = "bridge_vis"  # Bridge command that opens/changes KiCad visually+IPC_TEST = "ipc_test"         # Run IPC test, show section+IPC_LAYER = "ipc_layer"       # Switch active layer (visible in pcbnew)+WIN_CLOSE = "win_close"       # Close a window by title substring++# Demo project paths (resolved at import time)+_TOUR_DIR = os.path.dirname(os.path.abspath(__file__))+_DEMO_DIR = os.path.join(_TOUR_DIR, "demo-project")+SCH_PATH = os.path.join(_DEMO_DIR, "demo-project.kicad_sch").replace("/", "\\")+PCB_PATH = os.path.join(_DEMO_DIR, "demo-project.kicad_pcb").replace("/", "\\")++TOUR_STEPS = [+    # ══════════════════════════════════════════════════════════════+    # Act 1: "Meet Your Design — Schematic"+    # ══════════════════════════════════════════════════════════════+    {+        "act": "Act 1 — Schematic",+        "title": "Welcome to the Gallia Tour",+        "description": (+            "Gallia gives AI agents full control over KiCad. "+            "Every step in this tour triggers a real action in "+            "KiCad — watch the editor windows on the left.\n\n"+            "Let's start by opening your schematic."+        ),+        "action": BRIDGE_VISUAL,+        "command": "open_schematic",+        "args": {"filePath": SCH_PATH},+        "wait_window": "Schematic Editor",+        "delay_before": 1.0,+        "delay_after": 4.0,+    },+    {+        "act": "Act 1 — Schematic",+        "title": "Schematic Statistics",+        "description": (+            "Parsing the schematic file to extract statistics — "+            "component counts, wires, labels, and hierarchy. "+            "The schematic is open on the left so you can "+            "visually verify the numbers."+        ),+        "action": BRIDGE,+        "command": "get_schematic_stats",+        "args": {"filePath": SCH_PATH},+        "delay_before": 1.0,+        "delay_after": 5.0,+    },+    {+        "act": "Act 1 — Schematic",+        "title": "Schematic Hierarchy",+        "description": (+            "Mapping the hierarchical sheet tree. Complex designs "+            "use sub-sheets to organize blocks. Look at the "+            "schematic — each sheet instance maps to a node here."+        ),+        "action": BRIDGE,+        "command": "get_schematic_hierarchy",+        "args": {"filePath": SCH_PATH},+        "delay_before": 1.0,+        "delay_after": 5.0,+    },+    {+        "act": "Act 1 — Schematic",+        "title": "List Components",+        "description": (+            "Extracting every component from the schematic. "+            "Each entry shows the reference designator, value, "+            "and library symbol — matching what you see placed "+            "on the schematic sheet."+        ),+        "action": BRIDGE,+        "command": "list_schematic_components",+        "args": {"filePath": SCH_PATH},+        "delay_before": 1.0,+        "delay_after": 5.0,+    },++    # ══════════════════════════════════════════════════════════════+    # Act 2: "PCB Layout"+    # ══════════════════════════════════════════════════════════════+    {+        "act": "Act 2 — PCB Layout",+        "title": "Open PCB Editor",+        "description": (+            "Closing the schematic and opening the PCB layout. "+            "This is where copper traces, footprints, and the "+            "physical board design live."+        ),+        "action": BRIDGE_VISUAL,+        "command": "open_board",+        "args": {"filePath": PCB_PATH},+        "close_first": "Schematic Editor",+        "wait_window": "PCB Editor",+        "delay_before": 1.0,+        "delay_after": 4.0,+    },+    {+        "act": "Act 2 — PCB Layout",+        "title": "PCB Statistics",+        "description": (+            "Board dimensions, layer count, track count, vias, "+            "and footprints. Look at the board on the left — "+            "these numbers describe exactly what you see."+        ),+        "action": BRIDGE,+        "command": "get_pcb_statistics",+        "args": {"filePath": PCB_PATH},+        "delay_before": 1.0,+        "delay_after": 5.0,+    },+    {+        "act": "Act 2 — PCB Layout",+        "title": "Net List",+        "description": (+            "Every electrical connection on the board is a net. "+            "Each colored ratsnest line in pcbnew represents one "+            "of these nets connecting pads together."+        ),+        "action": BRIDGE,+        "command": "list_pcb_nets",+        "args": {"filePath": PCB_PATH},+        "delay_before": 1.0,+        "delay_after": 5.0,+    },+    {+        "act": "Act 2 — PCB Layout",+        "title": "Bill of Materials",+        "description": (+            "The BOM lists every component needed to build this "+            "board. Each footprint you see placed on the PCB "+            "corresponds to a row in this table."+        ),+        "action": BRIDGE,+        "command": "generate_bom",+        "args": {"filePath": SCH_PATH},+        "delay_before": 1.0,+        "delay_after": 5.0,+    },++    # ══════════════════════════════════════════════════════════════+    # Act 3: "Live IPC Control"+    # ══════════════════════════════════════════════════════════════+    {+        "act": "Act 3 — Live IPC",+        "title": "IPC: Ping & Version",+        "description": (+            "Now talking directly to KiCad's running process via "+            "IPC (protobuf over NNG). This is a live connection — "+            "not file parsing. Ping confirms KiCad is listening."+        ),+        "action": IPC_TEST,+        "section": 1,+        "delay_before": 1.0,+        "delay_after": 4.0,+    },+    {+        "act": "Act 3 — Live IPC",+        "title": "IPC: Board Layers",+        "description": (+            "Querying enabled layers directly from KiCad's memory. "+            "These match the layer list in pcbnew's right panel. "+            "Next we'll switch between them live."+        ),+        "action": IPC_TEST,+        "section": 4,+        "delay_before": 1.0,+        "delay_after": 4.0,+    },+    {+        "act": "Act 3 — Live IPC",+        "title": "Switch to B.Cu (Back Copper)",+        "description": (+            "Watch KiCad! Sending an IPC command to switch the "+            "active layer to B.Cu. The layer selector in pcbnew "+            "will change and the board rendering will update."+        ),+        "action": IPC_LAYER,+        "layer_id": 34,+        "layer_name": "B.Cu",+        "delay_before": 1.5,+        "delay_after": 3.0,+    },+    {+        "act": "Act 3 — Live IPC",+        "title": "Switch to In1.Cu (Inner Layer)",+        "description": (+            "Switching to inner copper layer 1. The PCB editor "+            "highlights the active layer — watch the color "+            "change in the layer panel on the right."+        ),+        "action": IPC_LAYER,+        "layer_id": 4,+        "layer_name": "In1.Cu",+        "delay_before": 1.5,+        "delay_after": 3.0,+    },+    {+        "act": "Act 3 — Live IPC",+        "title": "Restore F.Cu (Front Copper)",+        "description": (+            "Back to front copper. An AI agent can switch layers "+            "to inspect routing, check clearances, or prepare "+            "specific layers for export."+        ),+        "action": IPC_LAYER,+        "layer_id": 3,+        "layer_name": "F.Cu",+        "delay_before": 1.5,+        "delay_after": 3.0,+    },++    # ══════════════════════════════════════════════════════════════+    # Act 4: "Symbol Library"+    # ══════════════════════════════════════════════════════════════+    {+        "act": "Act 4 — Libraries",+        "title": "Search Symbol Libraries",+        "description": (+            "Searching KiCad's symbol libraries for 'opamp'. "+            "KiCad ships with hundreds of libraries — this "+            "command searches across all of them instantly."+        ),+        "action": BRIDGE,+        "command": "search_symbols",+        "args": {"query": "opamp"},+        "delay_before": 1.0,+        "delay_after": 4.0,+    },+    {+        "act": "Act 4 — Libraries",+        "title": "Open Symbol Editor: LM358",+        "description": (+            "Opening the Symbol Editor and loading the LM358 "+            "dual op-amp. Watch KiCad — the symbol will appear "+            "with all its pins, ready for inspection."+        ),+        "action": BRIDGE_VISUAL,+        "command": "open_symbol_editor",+        "args": {"symbolName": "LM358", "libraryName": "Amplifier_Operational"},+        "close_first": "PCB Editor",+        "wait_window": "Symbol Editor",+        "delay_before": 1.0,+        "delay_after": 5.0,+    },+    {+        "act": "Act 4 — Libraries",+        "title": "Symbol Pin Details",+        "description": (+            "Extracting pin information for the LM358. Each pin "+            "you see in the Symbol Editor on the left matches "+            "a row here — name, number, electrical type."+        ),+        "action": BRIDGE,+        "command": "get_symbol_info",+        "args": {"symbol_name": "Amplifier_Operational:LM358"},+        "delay_before": 1.0,+        "delay_after": 5.0,+    },++    # ══════════════════════════════════════════════════════════════+    # Act 5: "Design Checks"+    # ══════════════════════════════════════════════════════════════+    {+        "act": "Act 5 — Design Checks",+        "title": "Design Rule Check (DRC)",+        "description": (+            "Running DRC against the PCB. This checks for "+            "manufacturing violations — clearance errors, "+            "unconnected nets, invalid track widths. "+            "Opening the board so you can see the layout."+        ),+        "action": BRIDGE_VISUAL,+        "command": "open_board",+        "args": {"filePath": PCB_PATH},+        "close_first": "Symbol Editor",+        "wait_window": "PCB Editor",+        "post_command": "run_drc",+        "post_args": {"filePath": PCB_PATH},+        "delay_before": 1.0,+        "delay_after": 5.0,+    },+    {+        "act": "Act 5 — Design Checks",+        "title": "Electrical Rules Check (ERC)",+        "description": (+            "Running ERC against the schematic. This validates "+            "pin connections, power flags, and net consistency. "+            "Opening the schematic to show what's being checked."+        ),+        "action": BRIDGE_VISUAL,+        "command": "open_schematic",+        "args": {"filePath": SCH_PATH},+        "close_first": "PCB Editor",+        "wait_window": "Schematic Editor",+        "post_command": "run_erc",+        "post_args": {"filePath": SCH_PATH},+        "delay_before": 1.0,+        "delay_after": 5.0,+    },++    # ══════════════════════════════════════════════════════════════+    # Finale+    # ══════════════════════════════════════════════════════════════+    {+        "act": "Finale",+        "title": "Tour Complete!",+        "description": (+            "You've just seen Gallia control KiCad through:\n\n"+            "• 37 Python bridge commands (schematic, PCB, BOM, "+            "library search, DRC/ERC)\n\n"+            "• 15 Rust IPC commands (ping, version, layers, "+            "stackup, live layer switching)\n\n"+            "All available to AI agents for automated PCB "+            "engineering workflows."+        ),+        "action": DISPLAY,+        "delay_before": 1.0,+        "delay_after": 10.0,+    },+]
tour/tour.pyadded+716
@@ -0,0 +1,716 @@+"""KiCad Feature Tour — main orchestrator.++Usage:+    python tour.py [--speed fast|normal|slow] [--start N] [--no-ipc]+"""++import argparse+import json+import sys+import threading+import time++from narration import (+    NarrationPanel, GREEN, RED, YELLOW, BLUE, ACCENT,+    TEXT_MUTED, TEXT_SECONDARY, FG_DIM, FG_SECONDARY,+)+from steps import (+    TOUR_STEPS, DISPLAY, BRIDGE, BRIDGE_VISUAL, IPC_TEST, IPC_LAYER,+    WIN_CLOSE,+)+import bridge_client+import ipc_client+import win_manager+++# ── Speed multipliers ────────────────────────────────────────────+SPEED_MULT = {"fast": 0.5, "normal": 1.0, "slow": 1.5}+++# ── Data formatters ──────────────────────────────────────────────++def _extract_result(data: dict):+    """Extract the meaningful payload from a bridge response."""+    if "data" in data:+        return data["data"]+    if "result" in data:+        return data["result"]+    return data+++def format_json_response(data: dict, indent: int = 2) -> str:+    """Format a bridge response dict into readable text."""+    if not data:+        return "(no data)"+    if "error" in data and not data.get("success", True):+        return f"Error: {data['error']}"+    result = _extract_result(data)+    if isinstance(result, str):+        return result+    if isinstance(result, dict):+        return json.dumps(result, indent=indent, default=str)+    if isinstance(result, list):+        lines = []+        for i, item in enumerate(result[:30]):+            if isinstance(item, dict):+                line = json.dumps(item, default=str)+                if len(line) > 80:+                    line = line[:77] + "..."+                lines.append(f"  {line}")+            else:+                lines.append(f"  {item}")+        if len(result) > 30:+            lines.append(f"  ... and {len(result) - 30} more")+        return f"[{len(result)} items]\n" + "\n".join(lines)+    return str(result)+++def format_pcb_stats(data: dict) -> str:+    r = _extract_result(data)+    if isinstance(r, str):+        try:+            r = json.loads(r)+        except (json.JSONDecodeError, TypeError):+            return str(r)+    if not isinstance(r, dict):+        return format_json_response(data)+    lines = []+    if "board_dimensions" in r:+        d = r["board_dimensions"]+        lines.append(f"Board Size:     {d.get('width_mm', '?')} x {d.get('height_mm', '?')} mm")+    for key, label in [("layer_count", "Copper Layers"), ("track_count", "Tracks"),+                        ("via_count", "Vias"), ("pad_count", "Pads"),+                        ("footprint_count", "Footprints"), ("net_count", "Nets"),+                        ("zone_count", "Zones")]:+        if key in r:+            val = r[key]+            lines.append(f"{label + ':':16s} {val:,}" if isinstance(val, int) else f"{label + ':':16s} {val}")+    return "\n".join(lines) if lines else format_json_response(data)+++def format_schematic_stats(data: dict) -> str:+    r = _extract_result(data)+    if isinstance(r, str):+        try:+            r = json.loads(r)+        except (json.JSONDecodeError, TypeError):+            return str(r)+    if not isinstance(r, dict):+        return format_json_response(data)+    lines = []+    for key, label in [+        ("component_count", "Components"), ("symbol_count", "Symbols"),+        ("wire_count", "Wires"), ("label_count", "Labels"),+        ("global_label_count", "Global Labels"),+        ("hierarchical_label_count", "Hier. Labels"),+        ("sheet_count", "Sub-sheets"), ("junction_count", "Junctions"),+        ("no_connect_count", "No-connects"), ("bus_count", "Buses"),+        ("power_port_count", "Power Ports"),+    ]:+        if key in r:+            lines.append(f"{label + ':':20s} {r[key]}")+    return "\n".join(lines) if lines else format_json_response(data)+++def format_hierarchy(data: dict) -> str:+    r = _extract_result(data)+    if isinstance(r, str):+        try:+            r = json.loads(r)+        except (json.JSONDecodeError, TypeError):+            return str(r)++    def _tree(node, prefix="", is_last=True):+        lines = []+        connector = "+-- " if is_last else "|-- "+        name = node.get("name", node.get("sheet_name", "?"))+        filename = node.get("filename", node.get("file", ""))+        label = f"{name}"+        if filename:+            label += f"  ({filename})"+        lines.append(f"{prefix}{connector}{label}")+        children = node.get("children", node.get("sub_sheets", []))+        child_prefix = prefix + ("    " if is_last else "|   ")+        for i, child in enumerate(children):+            lines.extend(_tree(child, child_prefix, i == len(children) - 1))+        return lines++    if isinstance(r, dict):+        name = r.get("name", r.get("sheet_name", "Root"))+        children = r.get("children", r.get("sub_sheets", []))+        lines = [f"[Root] {name}"]+        for i, child in enumerate(children):+            lines.extend(_tree(child, "  ", i == len(children) - 1))+        return "\n".join(lines)+    elif isinstance(r, list):+        return "\n".join(+            format_hierarchy({"result": item}) if isinstance(item, dict) else str(item)+            for item in r+        )+    return format_json_response(data)+++def format_components(data: dict) -> str:+    """Format schematic component list."""+    r = _extract_result(data)+    if isinstance(r, str):+        try:+            r = json.loads(r)+        except (json.JSONDecodeError, TypeError):+            return str(r)+    items = r if isinstance(r, list) else r.get("components", []) if isinstance(r, dict) else []+    lines = [f"Total components: {len(items)}", ""]+    lines.append(f"{'Ref':8s} {'Value':15s} {'Library Symbol'}")+    lines.append("-" * 50)+    for item in items[:25]:+        ref = str(item.get("reference", item.get("ref", "?")))[:7]+        val = str(item.get("value", "?"))[:14]+        lib = str(item.get("library_symbol", item.get("symbol", item.get("lib_id", "?"))))+        if ":" in lib:+            lib = lib.split(":")[-1]+        lines.append(f"{ref:8s} {val:15s} {lib[:25]}")+    if len(items) > 25:+        lines.append(f"... and {len(items) - 25} more")+    return "\n".join(lines)+++def format_bom(data: dict) -> str:+    r = _extract_result(data)+    if isinstance(r, str):+        try:+            r = json.loads(r)+        except (json.JSONDecodeError, TypeError):+            return str(r)+    if isinstance(r, dict):+        items = r.get("items", r.get("components", []))+        summary = r.get("summary", {})+    elif isinstance(r, list):+        items = r+        summary = {}+    else:+        return format_json_response(data)+    lines = []+    if summary:+        lines.append(f"Unique parts:  {summary.get('unique_count', len(items))}")+        lines.append(f"Total placed:  {summary.get('total_count', '?')}")+        lines.append("")+    lines.append(f"{'Ref':10s} {'Value':15s} {'Footprint':25s}")+    lines.append("-" * 52)+    for item in items[:25]:+        ref = str(item.get("reference", item.get("ref", "?")))[:9]+        val = str(item.get("value", "?"))[:14]+        fp = str(item.get("footprint", "?")).split(":")[-1][:24]+        lines.append(f"{ref:10s} {val:15s} {fp:25s}")+    if len(items) > 25:+        lines.append(f"... and {len(items) - 25} more components")+    return "\n".join(lines)+++def format_nets(data: dict) -> str:+    r = _extract_result(data)+    if isinstance(r, str):+        try:+            r = json.loads(r)+        except (json.JSONDecodeError, TypeError):+            return str(r)+    nets = r if isinstance(r, list) else r.get("nets", []) if isinstance(r, dict) else []+    lines = [f"Total nets: {len(nets)}", ""]+    lines.append(f"{'ID':6s} {'Net Name'}")+    lines.append("-" * 40)+    for net in nets[:30]:+        if isinstance(net, dict):+            nid = str(net.get("id", net.get("number", "?")))+            name = net.get("name", "unnamed")+        else:+            nid = "-"+            name = str(net)+        lines.append(f"{nid:6s} {name}")+    if len(nets) > 30:+        lines.append(f"... and {len(nets) - 30} more nets")+    return "\n".join(lines)+++def format_search_results(data: dict) -> str:+    r = _extract_result(data)+    if isinstance(r, str):+        try:+            r = json.loads(r)+        except (json.JSONDecodeError, TypeError):+            return str(r)+    results = r if isinstance(r, list) else r.get("results", r.get("symbols", [])) if isinstance(r, dict) else []+    lines = [f"Found {len(results)} matching symbols:", ""]+    for item in results[:20]:+        if isinstance(item, dict):+            name = item.get("name", item.get("symbol_name", "?"))+            lib = item.get("library", item.get("lib_name", ""))+            desc = item.get("description", "")+            lines.append(f"  {lib}:{name}" if lib else f"  {name}")+            if desc:+                lines.append(f"    {desc[:60]}")+        else:+            lines.append(f"  {item}")+    return "\n".join(lines)+++def format_symbol_info(data: dict) -> str:+    r = _extract_result(data)+    if isinstance(r, str):+        try:+            r = json.loads(r)+        except (json.JSONDecodeError, TypeError):+            return str(r)+    if not isinstance(r, dict):+        return format_json_response(data)+    lines = []+    name = r.get("name", r.get("symbol_name", "?"))+    lines.append(f"Symbol: {name}")+    if "description" in r:+        lines.append(f"  {r['description']}")+    lines.append("")+    pins = r.get("pins", [])+    if pins:+        lines.append(f"{'Pin':6s} {'Name':15s} {'Type':12s} {'Dir'}")+        lines.append("-" * 45)+        for pin in pins[:20]:+            num = str(pin.get("number", pin.get("num", "?")))[:5]+            pname = str(pin.get("name", "?"))[:14]+            ptype = str(pin.get("electrical_type", pin.get("type", "?")))[:11]+            direction = str(pin.get("direction", pin.get("orientation", "")))+            lines.append(f"{num:6s} {pname:15s} {ptype:12s} {direction}")+        if len(pins) > 20:+            lines.append(f"... and {len(pins) - 20} more pins")+    return "\n".join(lines)+++def format_drc_erc(data: dict, check_type: str = "DRC") -> str:+    r = _extract_result(data)+    if isinstance(r, str):+        try:+            r = json.loads(r)+        except (json.JSONDecodeError, TypeError):+            return str(r)+    if not isinstance(r, dict):+        return format_json_response(data)+    lines = []+    violations = r.get("violations", r.get("errors", []))+    warnings = r.get("warnings", [])+    total = len(violations) + len(warnings)+    if total == 0:+        lines.append(f"{check_type} passed with no violations!")+    else:+        lines.append(f"{check_type}: {len(violations)} violations, {len(warnings)} warnings")+        lines.append("")+        for v in violations[:15]:+            if isinstance(v, dict):+                sev = v.get("severity", "error")+                msg = v.get("message", v.get("description", str(v)))+                lines.append(f"  [{sev}] {msg[:70]}")+            else:+                lines.append(f"  {str(v)[:70]}")+        for w in warnings[:10]:+            if isinstance(w, dict):+                msg = w.get("message", w.get("description", str(w)))+                lines.append(f"  [warn] {msg[:70]}")+            else:+                lines.append(f"  [warn] {str(w)[:70]}")+        remaining = total - min(len(violations), 15) - min(len(warnings), 10)+        if remaining > 0:+            lines.append(f"  ... and {remaining} more")+    return "\n".join(lines)+++# Command-specific formatters+FORMATTERS = {+    "get_pcb_statistics": format_pcb_stats,+    "get_schematic_stats": format_schematic_stats,+    "get_schematic_hierarchy": format_hierarchy,+    "list_schematic_components": format_components,+    "generate_bom": format_bom,+    "list_pcb_nets": format_nets,+    "search_symbols": format_search_results,+    "get_symbol_info": format_symbol_info,+    "run_drc": lambda d: format_drc_erc(d, "DRC"),+    "run_erc": lambda d: format_drc_erc(d, "ERC"),+}+++class TourRunner:+    """Runs the tour steps on a background thread, updates the narration panel."""++    def __init__(self, panel: NarrationPanel, speed: float = 1.0,+                 start_step: int = 1, skip_ipc: bool = False):+        self.panel = panel+        self.speed = speed+        self.start_step = start_step+        self.skip_ipc = skip_ipc++        self._pause_event = threading.Event()+        self._pause_event.set()+        self._skip_flag = False+        self._quit_flag = False+        self._ipc_cache: dict = {}++    def start(self):+        t = threading.Thread(target=self._run, daemon=True)+        t.start()++    def pause(self):+        self._pause_event.clear()++    def resume(self):+        self._pause_event.set()++    def skip(self):+        self._skip_flag = True+        self._pause_event.set()++    def quit(self):+        self._quit_flag = True+        self._pause_event.set()++    # ── Internal ─────────────────────────────────────────────────++    def _sleep(self, seconds: float):+        end = time.monotonic() + seconds * self.speed+        while time.monotonic() < end:+            if self._quit_flag or self._skip_flag:+                return+            self._pause_event.wait(timeout=0.2)++    def _update_panel(self, func, *args, **kwargs):+        self.panel.root.after(0, lambda: func(*args, **kwargs))++    def _run(self):+        total = len(TOUR_STEPS)+        self._preflight()++        for i, step in enumerate(TOUR_STEPS):+            step_num = i + 1+            if step_num < self.start_step:+                continue+            if self._quit_flag:+                break++            self._skip_flag = False+            self._update_panel(+                self.panel.set_step, step_num, total,+                step["title"], step["description"], step.get("act", ""),+            )++            self._sleep(step.get("delay_before", 1.0))+            if self._quit_flag:+                break+            if self._skip_flag:+                continue++            # Bring KiCad to foreground before every step so user sees the action+            action = step["action"]+            if action not in (DISPLAY,):+                try:+                    win_manager.position_kicad_left(self.panel.width)+                except Exception:+                    pass+                time.sleep(0.3)++            try:+                self._execute_step(step)+            except Exception as e:+                self._update_panel(self.panel.set_status, f"Error: {e}", RED)+                self._update_panel(self.panel.set_data, str(e))+                self._update_panel(self.panel.set_data_color, RED)++            if self._quit_flag:+                break+            if self._skip_flag:+                continue++            self._sleep(step.get("delay_after", 3.0))++        if not self._quit_flag:+            self._update_panel(self.panel.set_status, "Tour complete!", GREEN)++    def _preflight(self):+        self._update_panel(self.panel.set_status, "Preflight checks...", YELLOW)++        health = bridge_client.health_check()+        if health:+            self._update_panel(+                self.panel.set_data,+                f"Bridge server: ONLINE\n  URL: {bridge_client.BRIDGE_URL}"+            )+            self._update_panel(self.panel.set_data_color, GREEN)+        else:+            self._update_panel(+                self.panel.set_data,+                f"Bridge server: OFFLINE\n  URL: {bridge_client.BRIDGE_URL}\n"+                "  Start with: python plugins/kicad/server.py"+            )+            self._update_panel(self.panel.set_data_color, RED)++        time.sleep(1.5)++        if not self.skip_ipc:+            has_test = ipc_client.has_test_binary()+            has_layer = ipc_client.has_layer_binary()+            self._update_panel(+                self.panel.set_data,+                f"IPC test binary:  {'FOUND' if has_test else 'MISSING'}\n"+                f"IPC layer binary: {'FOUND' if has_layer else 'MISSING'}"+            )+            self._update_panel(+                self.panel.set_data_color, GREEN if (has_test and has_layer) else YELLOW+            )+            time.sleep(1.5)++        self._update_panel(self.panel.set_status, "Starting tour...", GREEN)+        time.sleep(1.0)++    def _execute_step(self, step: dict):+        action = step["action"]++        if action == DISPLAY:+            self._execute_display(step)+        elif action == BRIDGE:+            self._execute_bridge(step)+        elif action == BRIDGE_VISUAL:+            self._execute_bridge_visual(step)+        elif action == IPC_TEST:+            self._execute_ipc_test(step)+        elif action == IPC_LAYER:+            self._execute_ipc_layer(step)+        elif action == WIN_CLOSE:+            self._execute_win_close(step)++    def _execute_display(self, step: dict):+        self._update_panel(self.panel.set_status, "", TEXT_MUTED)+        if "Tour Complete" in step["title"]:+            self._update_panel(+                self.panel.set_data,+                "GALLIA FEATURE SUMMARY\n"+                "======================\n\n"+                "Track A  Python Bridge (37 commands):\n"+                "  Schematic: open, stats, hierarchy, components\n"+                "  PCB: open, stats, nets, BOM, DRC, ERC\n"+                "  Library: search, symbol info, footprints\n"+                "  Editors: symbol editor, footprint editor, 3D\n"+                "  Export: Gerber, drill, STEP, BOM\n\n"+                "Track B  Rust IPC (15 commands):\n"+                "  Connection: ping, version, get_api_version\n"+                "  Board: layers, stackup, nets, items\n"+                "  Editor: active layer, selection, refresh\n\n"+                "All commands available to AI agents\n"+                "via the Gallia bridge."+            )+            self._update_panel(self.panel.set_data_color, GREEN)++    def _execute_bridge(self, step: dict):+        """Execute a data-only bridge command and show results."""+        command = step["command"]+        args = step.get("args", {})++        self._update_panel(self.panel.set_status, f"bridge: {command}", ACCENT)++        result = bridge_client.send_command(command, args)+        formatter = FORMATTERS.get(command, format_json_response)+        display_text = formatter(result)++        is_ok = result.get("success", True) and "error" not in result+        self._update_panel(self.panel.set_data_color, GREEN if is_ok else RED)+        self._update_panel(+            self.panel.set_status,+            f"{command}  OK" if is_ok else f"{command}  Error",+            GREEN if is_ok else RED,+        )+        self._update_panel(self.panel.set_data, display_text)++    def _execute_bridge_visual(self, step: dict):+        """Execute a bridge command that opens/changes KiCad visually.++        Supports:+            close_first:   window title to close before opening+            command/args:  bridge command to send+            wait_window:   window title to wait for and position+            post_command:  optional second bridge command to run after (e.g. run_drc)+        """+        command = step["command"]+        args = step.get("args", {})++        # Close previous editor if requested+        close_first = step.get("close_first")+        if close_first:+            self._update_panel(self.panel.set_status, f"Closing {close_first}...", TEXT_SECONDARY)+            win_manager.close_window_by_title(close_first)+            time.sleep(1.5)++        # Send the visual command+        self._update_panel(self.panel.set_status, f"bridge: {command}", ACCENT)+        result = bridge_client.send_command(command, args)++        if result.get("error"):+            self._update_panel(self.panel.set_data, f"Error: {result['error']}")+            self._update_panel(self.panel.set_data_color, RED)+            self._update_panel(self.panel.set_status, f"{command} failed", RED)+            return++        # Wait for the window to appear+        wait_window = step.get("wait_window")+        if wait_window:+            self._update_panel(self.panel.set_status, f"Waiting for {wait_window}...", YELLOW)+            time.sleep(3.0)+            hwnd = win_manager.position_kicad_left(self.panel.width)+            if hwnd:+                self._update_panel(+                    self.panel.set_status, f"{wait_window} ready", GREEN,+                )+            else:+                self._update_panel(+                    self.panel.set_status, f"{wait_window} (window not positioned)", YELLOW,+                )++        # Run post-command if specified (e.g. run_drc after opening the board)+        post_cmd = step.get("post_command")+        if post_cmd:+            post_args = step.get("post_args", {})+            self._update_panel(self.panel.set_status, f"bridge: {post_cmd}", ACCENT)+            post_result = bridge_client.send_command(post_cmd, post_args)+            formatter = FORMATTERS.get(post_cmd, format_json_response)+            display_text = formatter(post_result)+            is_ok = post_result.get("success", True) and "error" not in post_result+            self._update_panel(self.panel.set_data_color, GREEN if is_ok else RED)+            self._update_panel(+                self.panel.set_status,+                f"{post_cmd}  OK" if is_ok else f"{post_cmd}  Error",+                GREEN if is_ok else RED,+            )+            self._update_panel(self.panel.set_data, display_text)+        else:+            # Show the open result+            formatter = FORMATTERS.get(command, format_json_response)+            self._update_panel(self.panel.set_data, formatter(result))+            self._update_panel(self.panel.set_data_color, GREEN)++    def _execute_ipc_test(self, step: dict):+        if self.skip_ipc:+            self._update_panel(self.panel.set_status, "IPC skipped (--no-ipc)", YELLOW)+            self._update_panel(self.panel.set_data, "IPC steps skipped.")+            return++        if not self._ipc_cache:+            self._update_panel(self.panel.set_status, "Running IPC test suite...", ACCENT)+            self._ipc_cache = ipc_client.run_ipc_test()+            if not self._ipc_cache:+                self._update_panel(self.panel.set_status, "IPC binary not found", RED)+                self._update_panel(self.panel.set_data, "test_kicad_ipc.exe not found.")+                self._update_panel(self.panel.set_data_color, RED)+                self.skip_ipc = True+                return++        section = step.get("section", 1)+        text = self._ipc_cache.get(section, f"(Section [{section}] not found)")+        self._update_panel(self.panel.set_data, text)+        self._update_panel(self.panel.set_data_color, GREEN)+        self._update_panel(self.panel.set_status, f"IPC section [{section}]", GREEN)++    def _execute_ipc_layer(self, step: dict):+        if self.skip_ipc:+            self._update_panel(self.panel.set_status, "IPC skipped", YELLOW)+            return++        layer_id = step["layer_id"]+        layer_name = step.get("layer_name", f"layer {layer_id}")++        self._update_panel(+            self.panel.set_status,+            f"IPC: set_active_layer({layer_id})  {layer_name}", ACCENT,+        )++        # Bring KiCad to front so the user sees the change+        win_manager.position_kicad_left(self.panel.width)+        time.sleep(0.5)++        result = ipc_client.set_active_layer(layer_id)+        if result.get("success"):+            self._update_panel(+                self.panel.set_data,+                f"Active layer: {layer_name}\n\n{result.get('output', '')}",+            )+            self._update_panel(self.panel.set_data_color, GREEN)+            self._update_panel(self.panel.set_status, f"Layer: {layer_name}", GREEN)+        else:+            self._update_panel(+                self.panel.set_data,+                f"Layer switch failed:\n{result.get('output', 'Unknown error')}",+            )+            self._update_panel(self.panel.set_data_color, RED)+            self._update_panel(self.panel.set_status, "Layer switch failed", RED)++    def _execute_win_close(self, step: dict):+        title = step["window_title"]+        self._update_panel(self.panel.set_status, f"Closing {title}...", TEXT_SECONDARY)+        success = win_manager.close_window_by_title(title)+        if success:+            self._update_panel(self.panel.set_data, f"{title} closed.")+            self._update_panel(self.panel.set_data_color, GREEN)+            self._update_panel(self.panel.set_status, "Closed", GREEN)+        else:+            self._update_panel(self.panel.set_data, f"'{title}' not found (may already be closed).")+            self._update_panel(self.panel.set_data_color, YELLOW)+            self._update_panel(self.panel.set_status, "Window not found", YELLOW)+++# ── Entry point ──────────────────────────────────────────────────++def main():+    parser = argparse.ArgumentParser(description="KiCad Feature Tour")+    parser.add_argument(+        "--speed", choices=["fast", "normal", "slow"], default="normal",+        help="Tour speed (fast=0.5x delays, slow=1.5x delays)",+    )+    parser.add_argument(+        "--start", type=int, default=1,+        help="Start at step N (1-based)",+    )+    parser.add_argument(+        "--no-ipc", action="store_true",+        help="Skip IPC steps (if binaries not available)",+    )+    args = parser.parse_args()+    speed = SPEED_MULT[args.speed]++    runner = None++    def on_pause():+        if runner:+            runner.pause()++    def on_resume():+        if runner:+            runner.resume()++    def on_skip():+        if runner:+            runner.skip()++    def on_quit():+        if runner:+            runner.quit()++    panel = NarrationPanel(+        width=480,+        on_pause=on_pause,+        on_resume=on_resume,+        on_skip=on_skip,+        on_quit=on_quit,+    )++    runner = TourRunner(+        panel=panel, speed=speed,+        start_step=args.start, skip_ipc=args.no_ipc,+    )+    runner.start()++    panel.root.mainloop()+++if __name__ == "__main__":+    main()
tour/tour_cli.pyadded+436
@@ -0,0 +1,436 @@+"""Unified CLI for the Gallia KiCad tour — used by Claude Code.++Subcommands:+    preflight               Check all prerequisites+    screenshot [path]       Capture desktop to PNG+    highlight x y w h       Show highlight overlay (non-blocking)+    bridge <cmd> [k=v ...]  Send bridge command, print JSON+    ipc-test [--section N]  Run IPC test suite+    ipc-layer <id>          Set active PCB layer via IPC+    window <action> <title> Window management (find/close/front/position/wait)++Zero pip deps — only stdlib.+"""++import argparse+import json+import os+import subprocess+import sys+import time++# Ensure tour package is importable+TOUR_DIR = os.path.dirname(os.path.abspath(__file__))+sys.path.insert(0, TOUR_DIR)++PYTHON = sys.executable+++# ═══════════════════════════════════════════════════════════════════+# Subcommands+# ═══════════════════════════════════════════════════════════════════++def cmd_preflight(_args):+    """Check all tour prerequisites."""+    from bridge_client import health_check+    from ipc_client import has_test_binary, has_layer_binary+    import win_manager++    print("=== Gallia Tour Preflight ===\n")++    # Bridge server+    health = health_check()+    status = "ONLINE" if health else "OFFLINE"+    print(f"Bridge server:    {status}")+    if health:+        print(f"  URL: http://127.0.0.1:8772")++    # IPC binaries+    print(f"IPC test binary:  {'FOUND' if has_test_binary() else 'MISSING'}")+    print(f"IPC layer binary: {'FOUND' if has_layer_binary() else 'MISSING'}")++    # Screen size+    sw, sh = win_manager.get_screen_size()+    print(f"Screen size:      {sw} x {sh}")++    # KiCad windows+    found_kicad = False+    for name in ["KiCad", "Schematic Editor", "PCB Editor",+                  "Symbol Editor", "Footprint Editor"]:+        wins = win_manager.find_windows_by_title(name)+        if wins:+            title_ascii = wins[0][1].encode("ascii", "replace").decode()+            print(f"Window [{name}]:    OPEN - {title_ascii}")+            found_kicad = True+    if not found_kicad:+        print("KiCad windows:    none open")++    # Demo project+    demo = os.path.join(TOUR_DIR, "demo-project")+    sch = os.path.join(demo, "demo-project.kicad_sch")+    pcb = os.path.join(demo, "demo-project.kicad_pcb")+    print(f"\nDemo schematic:   {'EXISTS' if os.path.exists(sch) else 'MISSING'}")+    print(f"Demo PCB:         {'EXISTS' if os.path.exists(pcb) else 'MISSING'}")+    print(f"Demo dir:         {demo}")++    # Print paths for Claude to use+    if os.path.exists(sch):+        print(f"\nSCH_PATH = {sch}")+    if os.path.exists(pcb):+        print(f"PCB_PATH = {pcb}")+++def cmd_screenshot(args):+    """Capture the desktop to a PNG file."""+    from screenshot import capture_screen++    path = args.output+    if not path:+        temp = os.environ.get("TEMP", os.environ.get("TMP", "."))+        path = os.path.join(temp, "gallia_tour_screenshot.png")++    capture_screen(path)+    # Print path so Claude can read it+    print(path)+++def cmd_highlight(args):+    """Show a highlight overlay. Spawns a separate process (non-blocking)."""+    script = os.path.join(TOUR_DIR, "highlight.py")+    cmd = [+        PYTHON, script,+        str(args.x), str(args.y), str(args.width), str(args.height),+    ]+    if args.color:+        cmd.extend(["--color", args.color])+    if args.duration:+        cmd.extend(["--duration", str(args.duration)])+    if args.label:+        cmd.extend(["--label", args.label])+    if args.thickness:+        cmd.extend(["--thickness", str(args.thickness)])++    # Spawn without a console window on Windows+    flags = 0+    if os.name == "nt":+        flags = subprocess.CREATE_NO_WINDOW+    subprocess.Popen(cmd, creationflags=flags)+    print(f"Highlight: ({args.x}, {args.y}) {args.width}x{args.height}"+          f" color={args.color} duration={args.duration}ms"+          + (f' label="{args.label}"' if args.label else ""))+++def cmd_bridge(args):+    """Send a command to the KiCad bridge server."""+    from bridge_client import send_command, health_check++    if args.command == "health":+        result = health_check()+        if result:+            print(json.dumps(result, indent=2))+        else:+            print('{"status": "offline"}')+        return++    # Parse key=value argument pairs+    cmd_args = {}+    for item in (args.args or []):+        key, _, val = item.partition("=")+        if not val:+            print(f"Warning: skipping malformed arg '{item}' (expected key=value)")+            continue+        cmd_args[key] = val++    result = send_command(args.command, cmd_args)+    print(json.dumps(result, indent=2, default=str))+++def cmd_ipc_test(args):+    """Run the IPC test suite and print output."""+    from ipc_client import run_ipc_test, has_test_binary++    if not has_test_binary():+        print("ERROR: test_kicad_ipc.exe not found")+        print("Build with: cargo build --release --example test_kicad_ipc")+        sys.exit(1)++    sections = run_ipc_test()+    if args.section is not None:+        text = sections.get(args.section)+        if text:+            print(text)+        else:+            print(f"Section [{args.section}] not found in output")+            print(f"Available sections: {sorted(sections.keys())}")+    else:+        for k in sorted(sections.keys()):+            print(sections[k])+            print()+++def cmd_ipc_layer(args):+    """Set KiCad's active PCB layer via IPC."""+    from ipc_client import set_active_layer, has_layer_binary++    if not has_layer_binary():+        print("ERROR: ipc_set_layer.exe not found")+        print("Build with: cargo build --release --example ipc_set_layer")+        sys.exit(1)++    # Layer name map for user-friendly output+    layer_names = {3: "F.Cu", 4: "In1.Cu", 5: "In2.Cu", 34: "B.Cu"}+    name = layer_names.get(args.layer_id, f"layer {args.layer_id}")++    result = set_active_layer(args.layer_id)+    if result.get("success"):+        print(f"Active layer set to: {name} (id={args.layer_id})")+        if result.get("output"):+            print(result["output"])+    else:+        print(f"ERROR: Failed to set layer to {name}")+        print(result.get("output", "Unknown error"))+        sys.exit(1)+++def cmd_window(args):+    """Window management: find, close, front, position, wait."""+    import win_manager++    if args.action == "find":+        windows = win_manager.find_windows_by_title(args.title)+        if windows:+            for hwnd, title in windows:+                safe = title.encode("ascii", "replace").decode()+                print(f"  hwnd={hwnd}  title={safe}")+        else:+            print(f"No windows matching '{args.title}'")++    elif args.action == "close":+        ok = win_manager.close_window_by_title(args.title)+        print("Closed" if ok else f"Window not found: '{args.title}'")++    elif args.action == "front":+        windows = win_manager.find_windows_by_title(args.title)+        if windows:+            win_manager.bring_to_front(windows[0][0])+            safe = windows[0][1].encode("ascii", "replace").decode()+            print(f"Brought to front: {safe}")+        else:+            print(f"Window not found: '{args.title}'")++    elif args.action == "position":+        if args.x is None or args.y is None or args.w is None or args.h is None:+            print("ERROR: position requires x y w h arguments")+            sys.exit(1)+        windows = win_manager.find_windows_by_title(args.title)+        if windows:+            win_manager.position_window(windows[0][0], args.x, args.y,+                                        args.w, args.h)+            safe = windows[0][1].encode("ascii", "replace").decode()+            print(f"Positioned: {safe} at ({args.x},{args.y}) "+                  f"{args.w}x{args.h}")+        else:+            print(f"Window not found: '{args.title}'")++    elif args.action == "wait":+        timeout = args.timeout or 15+        deadline = time.monotonic() + timeout+        while time.monotonic() < deadline:+            windows = win_manager.find_windows_by_title(args.title)+            if windows:+                safe = windows[0][1].encode("ascii", "replace").decode()+                print(f"Found: {safe}")+                return+            time.sleep(0.5)+        print(f"Timeout ({timeout}s) waiting for: '{args.title}'")+        sys.exit(1)++    elif args.action == "screen-size":+        sw, sh = win_manager.get_screen_size()+        print(f"{sw} {sh}")+++def cmd_panel(args):+    """Control the narration side panel."""+    import urllib.request++    port = args.port or 8799+    base = f"http://127.0.0.1:{port}"++    if args.action == "start":+        # Launch narration.py as a background process+        script = os.path.join(TOUR_DIR, "narration.py")+        cmd = [PYTHON, script, "--port", str(port)]+        # On Windows, use `pythonw.exe` (windowless Python) to launch+        # the tkinter GUI without a console window. Falls back to+        # regular python.exe if pythonw doesn't exist.+        pythonw = PYTHON.replace("python.exe", "pythonw.exe")+        if os.path.exists(pythonw):+            cmd[0] = pythonw+        proc = subprocess.Popen(cmd)++        # Wait for the HTTP server to come up+        for _ in range(30):+            try:+                req = urllib.request.Request(f"{base}/health")+                with urllib.request.urlopen(req, timeout=1) as resp:+                    if resp.status == 200:+                        print(f"Panel started (pid={proc.pid}, port={port})")+                        return+            except Exception:+                time.sleep(0.3)+        print(f"Panel process started (pid={proc.pid}) but health check timed out")++    elif args.action == "update":+        payload = {}+        if args.step is not None:+            payload["step"] = args.step+        if args.total is not None:+            payload["total"] = args.total+        if args.title:+            payload["title"] = args.title+        if args.description:+            payload["description"] = args.description.replace("\\n", "\n")+        if args.act:+            payload["act"] = args.act+        if args.status:+            payload["status"] = args.status+        if args.status_color:+            payload["status_color"] = args.status_color+        if args.data:+            # Convert literal \n sequences to actual newlines+            payload["data"] = args.data.replace("\\n", "\n")+        if args.data_color:+            payload["data_color"] = args.data_color++        data = json.dumps(payload).encode("utf-8")+        req = urllib.request.Request(+            f"{base}/update", data=data,+            headers={"Content-Type": "application/json"},+            method="POST",+        )+        try:+            with urllib.request.urlopen(req, timeout=5):+                print("Panel updated")+        except Exception as e:+            print(f"Panel update failed: {e}")++    elif args.action == "stop":+        req = urllib.request.Request(f"{base}/quit", method="POST",+                                     data=b"{}")+        req.add_header("Content-Type", "application/json")+        try:+            with urllib.request.urlopen(req, timeout=5):+                print("Panel stopped")+        except Exception:+            print("Panel may already be stopped")++    elif args.action == "health":+        try:+            req = urllib.request.Request(f"{base}/health")+            with urllib.request.urlopen(req, timeout=3) as resp:+                print(json.loads(resp.read().decode()))+        except Exception:+            print("Panel not running")+++# ═══════════════════════════════════════════════════════════════════+# Argument parser+# ═══════════════════════════════════════════════════════════════════++def build_parser():+    parser = argparse.ArgumentParser(+        description="Gallia KiCad Tour CLI — used by Claude Code",+        formatter_class=argparse.RawDescriptionHelpFormatter,+    )+    sub = parser.add_subparsers(dest="cmd")++    # preflight+    sub.add_parser("preflight", help="Check all prerequisites")++    # screenshot+    p = sub.add_parser("screenshot", help="Capture desktop screenshot")+    p.add_argument("output", nargs="?", help="Output PNG path")++    # highlight+    p = sub.add_parser("highlight", help="Show highlight overlay on screen")+    p.add_argument("x", type=int, help="X position")+    p.add_argument("y", type=int, help="Y position")+    p.add_argument("width", type=int, help="Width of highlight")+    p.add_argument("height", type=int, help="Height of highlight")+    p.add_argument("--color", default="#00b8b1", help="Border color")+    p.add_argument("--duration", type=int, default=5000, help="Duration ms")+    p.add_argument("--label", default="", help="Label text")+    p.add_argument("--thickness", type=int, default=3, help="Border px")++    # bridge+    p = sub.add_parser("bridge", help="Send bridge command")+    p.add_argument("command", help="Command name (or 'health')")+    p.add_argument("args", nargs="*", help="key=value pairs")++    # ipc-test+    p = sub.add_parser("ipc-test", help="Run IPC test suite")+    p.add_argument("--section", type=int, help="Show only section N")++    # ipc-layer+    p = sub.add_parser("ipc-layer", help="Set active PCB layer via IPC")+    p.add_argument("layer_id", type=int,+                   help="Layer ID (F.Cu=3, In1.Cu=4, In2.Cu=5, B.Cu=34)")++    # window+    p = sub.add_parser("window", help="Window management")+    p.add_argument("action",+                   choices=["find", "close", "front", "position", "wait",+                            "screen-size"],+                   help="Action to perform")+    p.add_argument("title", nargs="?", default="",+                   help="Window title substring")+    p.add_argument("x", type=int, nargs="?", help="X position")+    p.add_argument("y", type=int, nargs="?", help="Y position")+    p.add_argument("w", type=int, nargs="?", help="Width")+    p.add_argument("h", type=int, nargs="?", help="Height")+    p.add_argument("--timeout", type=int, default=15,+                   help="Wait timeout in seconds")++    # panel+    p = sub.add_parser("panel", help="Control the narration side panel")+    p.add_argument("action", choices=["start", "update", "stop", "health"],+                   help="Panel action")+    p.add_argument("--port", type=int, default=8799, help="Panel HTTP port")+    p.add_argument("--step", type=int, help="Current step number")+    p.add_argument("--total", type=int, help="Total steps")+    p.add_argument("--title", help="Step title")+    p.add_argument("--description", help="Step description / narration")+    p.add_argument("--act", help="Act name")+    p.add_argument("--status", help="Status text")+    p.add_argument("--status-color", dest="status_color", help="Status color")+    p.add_argument("--data", help="Data area content")+    p.add_argument("--data-color", dest="data_color", help="Data area color")++    return parser+++def main():+    parser = build_parser()+    args = parser.parse_args()++    handlers = {+        "preflight": cmd_preflight,+        "screenshot": cmd_screenshot,+        "highlight": cmd_highlight,+        "bridge": cmd_bridge,+        "ipc-test": cmd_ipc_test,+        "ipc-layer": cmd_ipc_layer,+        "window": cmd_window,+        "panel": cmd_panel,+    }++    handler = handlers.get(args.cmd)+    if handler:+        handler(args)+    else:+        parser.print_help()+++if __name__ == "__main__":+    main()
tour/win_manager.pyadded+107
@@ -0,0 +1,107 @@+"""Win32 window management via ctypes — find, position, and close windows."""++import ctypes+import ctypes.wintypes+import time++user32 = ctypes.windll.user32++# Make DPI-aware so coordinates match physical pixels (screenshots/highlights)+try:+    user32.SetProcessDPIAware()+except Exception:+    pass++# Constants+SW_RESTORE = 9+SW_SHOWNOACTIVATE = 4+SWP_NOZORDER = 0x0004+SWP_NOACTIVATE = 0x0010+WM_CLOSE = 0x0010+VK_MENU = 0x12  # Alt key+KEYEVENTF_KEYUP = 0x0002+++def get_screen_size() -> tuple:+    """Return (width, height) of the primary monitor."""+    return user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)+++def find_windows_by_title(substring: str) -> list:+    """Find all visible windows containing substring in title.++    Returns list of (hwnd, title) tuples.+    """+    results = []++    def callback(hwnd, _lparam):+        if user32.IsWindowVisible(hwnd):+            length = user32.GetWindowTextLengthW(hwnd)+            if length > 0:+                buf = ctypes.create_unicode_buffer(length + 1)+                user32.GetWindowTextW(hwnd, buf, length + 1)+                if substring.lower() in buf.value.lower():+                    results.append((hwnd, buf.value))+        return True++    WNDENUMPROC = ctypes.WINFUNCTYPE(+        ctypes.c_bool, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM+    )+    user32.EnumWindows(WNDENUMPROC(callback), 0)+    return results+++def position_window(hwnd: int, x: int, y: int, w: int, h: int):+    """Move and resize a window."""+    user32.ShowWindow(hwnd, SW_RESTORE)+    user32.SetWindowPos(hwnd, 0, x, y, w, h, SWP_NOZORDER)+++def bring_to_front(hwnd: int):+    """Bring a window to the foreground using the Alt-key trick."""+    # Press and release Alt to allow SetForegroundWindow from background+    user32.keybd_event(VK_MENU, 0, 0, 0)+    user32.keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0)+    user32.SetForegroundWindow(hwnd)+++def close_window_by_title(substring: str, timeout: float = 5.0) -> bool:+    """Find and close the first window matching the title substring."""+    windows = find_windows_by_title(substring)+    for hwnd, _title in windows:+        user32.PostMessageW(hwnd, WM_CLOSE, 0, 0)+        deadline = time.monotonic() + timeout+        while time.monotonic() < deadline:+            time.sleep(0.3)+            if not user32.IsWindow(hwnd) or not user32.IsWindowVisible(hwnd):+                return True+    return False+++def position_kicad_left(narration_width: int = 480) -> int | None:+    """Find the active KiCad editor and position it on the left side.++    Returns the hwnd if found, None otherwise.+    """+    screen_w, screen_h = get_screen_size()+    kicad_w = screen_w - narration_width++    # Try to find KiCad windows in priority order+    for title_part in [+        "PCB Editor",+        "pcbnew",+        "Schematic Editor",+        "eeschema",+        "Symbol Editor",+        "Footprint Editor",+        "KiCad",+    ]:+        windows = find_windows_by_title(title_part)+        for hwnd, title in windows:+            # Skip tiny windows and the narration panel itself+            if "Adom" in title and "Tour" in title:+                continue+            position_window(hwnd, 0, 0, kicad_w, screen_h)+            bring_to_front(hwnd)+            return hwnd+    return None