From 29e832061b26f1d856dcec6911f0b1546510ab15 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Mon, 6 Apr 2026 10:00:41 +0100 Subject: [PATCH 001/166] migration spec and plan init --- CLAUDE.md | 71 +++++++++ MIGRATION.md | 110 ++++++++++++++ PLAN.md | 191 +++++++++++++++++++++++ SPEC.md | 418 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 790 insertions(+) create mode 100644 CLAUDE.md create mode 100644 MIGRATION.md create mode 100644 PLAN.md create mode 100644 SPEC.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..df93f6a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,71 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Active Migration + +This repository is being migrated from R/Shiny to FastAPI + Vite + TypeScript + Three.js (npm). + +- **`SPEC.md`** — architecture decisions, chosen stack, design patterns, API contract, and rationale. Read this first to understand why things are structured the way they are. +- **`PLAN.md`** — phased implementation checklist with checkboxes. Check this to see what has been done and what remains before starting any work. +- **`MIGRATION.md`** — maps each old R/Shiny file to its new equivalent; old files are deleted only after their replacement is tested. + +If you are on the `v3` branch, the presence of old R/Shiny files (`server.R`, `functions/`, `views/`, `www/js/`) means those pieces have not been ported yet — they are the living specification for what the new code should do. + +## Running the App + +**From RStudio:** +1. Open `Arena3Dweb.Rproj` +2. Open `server.R`, select "Run External", click "Run App" + +**Via Docker:** +```bash +docker pull pavlopouloslab/arena3dweb +docker run -p 3838:3838 pavlopouloslab/arena3dweb +``` + +**From R CLI:** +```r +shiny::runApp('.') +``` + +**Required R packages:** +```r +install.packages(c("shiny", "shinyjs", "shinythemes", "igraph", "RColorBrewer", "jsonlite", "tidyr", "dplyr", "DT", "fst")) +``` + +## Architecture Overview + +This is an **R/Shiny + Three.js** web application for interactive 3D visualization of multilayered networks. + +### R/Shiny Layer (backend) +- `global.R` — loaded once; imports libraries +- `ui.R` — loads all JS/CSS, defines the navbar tab layout (Home, File, Layer Selection & Layouts, Scene Actions, Layer Actions, Node Actions, Edge Actions, View Data, FPS, Help) +- `server.R` — wires all `observeEvent` handlers; calls `initializeServerApp()` on startup +- `views/` — one R file per UI tab (e.g. `file.R`, `layer.R`), each exporting a `generate*Div()` function +- `functions/` — server-side logic split by domain: + - `input.R` — network file upload and validation + - `init.R` — app startup: pushes global constants to JS, attaches download handler + - `js_handling.R` — helpers to sync JS state back to Shiny inputs + - `render.R` — modal/error/warning rendering + - `general.R`, `reset.R`, `edges.R`, `vr.R` — domain-specific handlers + - `igraph/` — layout, clustering, topology metric calculations using igraph +- `config/` — R-side variables (`global_variables.R`, `server_variables.R`, `static_variables.R`, `ui_variables.R`) + +### JavaScript Layer (frontend, `www/js/`) +- **Three.js** (`three/three.js`) — core 3D rendering engine; `matrix4.js` and `drag_controls.js` are Three.js add-ons +- **Classes** (`classes/`) — `Scene`, `Layer`, `Node`, `Edge` — OOP wrappers around Three.js objects +- **Object actions** (`object_actions/`) — functions for each entity type: `screen.js`, `network.js`, `layout.js`, `layer.js`, `node.js`, `edge.js`, `labels.js`, `themes.js`, `canvas_controls.js`, `right_click_menu.js` +- **Shiny bridge** — `rshiny_handlers.js` registers all `Shiny.addCustomMessageHandler("handler_*", ...)` callbacks; `rshiny_update.js` sends data from JS back to Shiny +- **Config** (`config/`) — `global_variables.js` (runtime globals initialized from R via `handler_initializeGlobals`), `static_variables.js` (constants like color palettes, geometry sizes) +- `general.js` — utility functions; `event_listeners.js` — mouse/keyboard events; `on_page_load.js` — Three.js canvas setup + +### R ↔ JS Communication Pattern +- **R → JS**: `session$sendCustomMessage("handler_*", payload)` in R calls the registered handler in `rshiny_handlers.js` +- **JS → R**: Shiny input updates (e.g. `Shiny.setInputValue`) trigger `observeEvent` handlers in `server.R` +- Constants (MAX_LAYERS, MAX_EDGES, MAX_CHANNELS, channel colors) are defined in R config and pushed to JS globals at startup via `handler_initializeGlobals` + +### Network Data Model +- Networks are uploaded as TSV with mandatory columns: `SourceNode`, `SourceLayer`, `TargetNode`, `TargetLayer` (optional: `Weight`, `Channel`, edge color columns) +- Sessions are exported/imported as JSON containing full node/edge/layer state +- The REST API endpoint accepts a URL parameter to load a network directly from an external application diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..1c3f42f --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,110 @@ +# MIGRATION.md + +Maps every old R/Shiny file to its replacement in the new stack. +**Deletion = done signal:** old files are deleted only after their replacement is tested. + +See `PLAN.md` for the ordered implementation steps. + +--- + +## Backend (R → Python/FastAPI) + +| Old file | New file | Done | +|---|---|---| +| `config/server_variables.R` | `backend/app/config.py` | [ ] | +| `config/global_variables.R` | `backend/app/config.py` | [ ] | +| `config/static_variables.R` | `backend/app/config.py` | [ ] | +| `config/ui_variables.R` | *(absorbed into frontend config)* | [ ] | +| `functions/input.R` | `backend/app/services/parser.py` + `backend/app/routers/network.py` | [ ] | +| `functions/init.R` | `backend/app/routers/config.py` + `backend/app/main.py` | [ ] | +| `functions/general.R` | `backend/app/services/graph.py` | [ ] | +| `functions/reset.R` | *(stateless server — no equivalent needed)* | [ ] | +| `functions/render.R` | *(absorbed into FastAPI error responses)* | [ ] | +| `functions/js_handling.R` | *(absorbed into frontend EventBus)* | [ ] | +| `functions/edges.R` | `backend/app/routers/network.py` | [ ] | +| `functions/vr.R` | `backend/app/routers/session.py` | [ ] | +| `functions/igraph/general.R` | `backend/app/services/graph.py` | [ ] | +| `functions/igraph/layout.R` | `backend/app/services/algorithms/layouts/` | [ ] | +| `functions/igraph/cluster.R` | `backend/app/services/algorithms/clusters/` | [ ] | +| `functions/igraph/topology.R` | `backend/app/services/algorithms/topology/` | [ ] | + +--- + +## Frontend (JS → TypeScript/Vite) + +| Old file | New file | Done | +|---|---|---| +| `www/js/three/three.js` *(patched)* | `three` npm package + `src/three/` extensions | [ ] | +| `www/js/three/matrix4.js` | `three` npm package (bundled) | [ ] | +| `www/js/three/drag_controls.js` | `three/examples/jsm/controls/DragControls` (npm) | [ ] | +| `www/js/classes/Scene.js` | `frontend/src/classes/Scene.ts` | [ ] | +| `www/js/classes/Layer.js` | `frontend/src/classes/Layer.ts` | [ ] | +| `www/js/classes/Node.js` | `frontend/src/classes/Node.ts` | [ ] | +| `www/js/classes/Edge.js` | `frontend/src/classes/Edge.ts` | [ ] | +| `www/js/config/global_variables.js` | `frontend/src/config/global_variables.ts` + `GET /api/config` | [ ] | +| `www/js/config/static_variables.js` | `frontend/src/config/static_variables.ts` | [ ] | +| `www/js/object_actions/canvas_controls.js` | `frontend/src/actions/canvas_controls.ts` | [ ] | +| `www/js/object_actions/network.js` | `frontend/src/actions/network.ts` | [ ] | +| `www/js/object_actions/layout.js` | `frontend/src/actions/layout.ts` | [ ] | +| `www/js/object_actions/layer.js` | `frontend/src/actions/layer.ts` | [ ] | +| `www/js/object_actions/node.js` | `frontend/src/actions/node.ts` | [ ] | +| `www/js/object_actions/edge.js` | `frontend/src/actions/edge.ts` | [ ] | +| `www/js/object_actions/screen.js` | `frontend/src/actions/screen.ts` | [ ] | +| `www/js/object_actions/themes.js` | `frontend/src/actions/themes.ts` | [ ] | +| `www/js/object_actions/labels.js` | `frontend/src/actions/labels.ts` | [ ] | +| `www/js/object_actions/right_click_menu.js` | `frontend/src/actions/right_click_menu.ts` | [ ] | +| `www/js/rshiny_handlers.js` | `frontend/src/bus/index.ts` + `frontend/src/api/client.ts` | [ ] | +| `www/js/rshiny_update.js` | `frontend/src/api/client.ts` | [ ] | +| `www/js/general.js` | `frontend/src/utils.ts` | [ ] | +| `www/js/event_listeners.js` | `frontend/src/event_listeners.ts` | [ ] | +| `www/js/on_page_load.js` | `frontend/src/main.ts` | [ ] | +| `www/arena3dweb.css` | Bootstrap 5 + `frontend/src/style.css` | [ ] | + +--- + +## UI / Shell (R/Shiny → HTML + TypeScript) + +| Old file | New file | Done | +|---|---|---| +| `ui.R` | `frontend/index.html` | [ ] | +| `server.R` | `backend/app/main.py` + routers | [ ] | +| `global.R` | `backend/app/main.py` (lifespan) | [ ] | +| `views/home.R` | `frontend/src/ui/home.ts` | [ ] | +| `views/file.R` | `frontend/src/ui/file.ts` | [ ] | +| `views/layouts.R` | `frontend/src/ui/layouts.ts` | [ ] | +| `views/scene.R` | `frontend/src/ui/scene.ts` | [ ] | +| `views/layer.R` | `frontend/src/ui/layer.ts` | [ ] | +| `views/node.R` | `frontend/src/ui/node.ts` | [ ] | +| `views/edge.R` | `frontend/src/ui/edge.ts` | [ ] | +| `views/data.R` | `frontend/src/ui/data.ts` | [ ] | +| `views/fps.R` | `frontend/src/ui/fps.ts` | [ ] | +| `views/help.R` | `frontend/src/ui/help.ts` | [ ] | +| `views/footer.R` | `frontend/index.html` (static footer) | [ ] | + +--- + +## Removed (no equivalent needed) + +| Old file | Reason | +|---|---| +| `Arena3Dweb.Rproj` | R project file — not applicable | +| `Rprofile.site` | R startup config — not applicable | +| `config/ui_variables.R` | Shiny UI helpers absorbed into frontend | +| `functions/reset.R` | Stateless server has no session to reset | +| `functions/render.R` | FastAPI handles errors via HTTP status codes + JSON | +| `functions/js_handling.R` | Shiny checkbox sync — replaced by frontend state store | + +--- + +## Algorithm Notes + +| Algorithm | Old (igraph) | New (NetworkX) | Exact match? | +|---|---|---|---| +| Force-directed layout | `layout_with_fr()` | `nx.spring_layout()` | Approximate | +| Circular layout | `layout_in_circle()` | `nx.circular_layout()` | Yes | +| Kamada-Kawai layout | `layout_with_kk()` | `nx.kamada_kawai_layout()` | Yes | +| Louvain clustering | `cluster_louvain()` | `community.best_partition()` | Yes | +| Walktrap clustering | `cluster_walktrap()` | `nx.community.asyn_lpa_communities()` | No — LPA approximation; verify output matches or use `igraph` Python bindings | +| Degree | `degree()` | `nx.degree_centrality()` | Yes | +| Betweenness centrality | `betweenness()` | `nx.betweenness_centrality()` | Yes | +| Clustering coefficient | `transitivity()` | `nx.clustering()` | Yes | diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..7673e8e --- /dev/null +++ b/PLAN.md @@ -0,0 +1,191 @@ +# Arena3Dweb v3 — Implementation Plan + +Tracks migration from R/Shiny to FastAPI + Vite + TypeScript + Three.js (npm). +See `SPEC.md` for architecture decisions and rationale. +See `MIGRATION.md` for the old-file → new-file deletion checklist. + +**Rule:** Each phase must be independently runnable and tested before the next begins. +**Done signal:** Old source files are deleted only after their replacement is tested. + +--- + +## Phase 1 — Scaffolding & Tooling + +- [ ] Create `v3` branch from `main` +- [ ] Tag current `main` as `v2-legacy` +- [ ] Scaffold `backend/` directory structure +- [ ] Scaffold `frontend/` directory structure +- [ ] Set up `backend/pyproject.toml` — Ruff + mypy config +- [ ] Set up `frontend/package.json` — Vite + TypeScript + ESLint + Prettier + Vitest +- [ ] Set up `frontend/vite.config.ts` — proxy `/api` to `localhost:8000` in dev +- [ ] Set up `frontend/tsconfig.json` +- [ ] Set up `docker-compose.yml` — hot-reload frontend + backend +- [ ] Set up `Dockerfile` — production single image (nginx + uvicorn) +- [ ] Set up `nginx/nginx.conf` — serve static build, proxy `/api` +- [ ] Set up `.github/workflows/ci.yml` — lint → typecheck → unit tests → E2E +- [ ] Set up `.pre-commit-config.yaml` — Ruff + ESLint/Prettier hooks +- [ ] Create `MIGRATION.md` with full old-file → new-file checklist + +--- + +## Phase 2 — Backend: Config Endpoint + +- [ ] Implement `backend/app/config.py` — port constants from `config/server_variables.R` +- [ ] Implement `GET /api/config` router +- [ ] Write pytest tests for `/api/config` +- [ ] Verify frontend can fetch and parse config response +- [ ] Delete `config/server_variables.R` + +--- + +## Phase 3 — Backend: Network Parsing + +- [ ] Implement Pydantic models — `NodeModel`, `EdgeModel`, `NetworkModel` (`models/network.py`) +- [ ] Implement `services/parser.py` — port TSV parsing and validation from `functions/input.R` +- [ ] Implement `POST /api/network` router +- [ ] Write pytest tests using `www/data/` TSV files as fixtures +- [ ] Verify validation errors match current behaviour (missing columns, non-numeric weights, empty channels) +- [ ] Delete `functions/input.R` + +--- + +## Phase 4 — Backend: Layout Algorithms + +- [ ] Implement abstract `LayoutStrategy` base class (`services/algorithms/base.py`) +- [ ] Implement `ForceDirectedLayout` — port `igraph::layout_with_fr()` +- [ ] Implement `CircularLayout` — port `igraph::layout_in_circle()` +- [ ] Implement `GridLayout` +- [ ] Implement `RandomLayout` +- [ ] Implement algorithm registry (`services/algorithms/registry.py`) +- [ ] Implement `services/graph.py` — `nx.Graph` construction helpers (port `functions/igraph/general.R`) +- [ ] Implement `POST /api/layout` router +- [ ] Write pytest tests for each layout strategy with fixture graphs +- [ ] Delete `functions/igraph/layout.R`, `functions/igraph/general.R` + +--- + +## Phase 5 — Backend: Clustering Algorithms + +- [ ] Implement `LouvainClustering` strategy — port `igraph::cluster_louvain()` +- [ ] Implement `WalktrapClustering` strategy — LPA approximation; note in MIGRATION.md if behaviour differs +- [ ] Implement `POST /api/cluster` router +- [ ] Write pytest tests for each clustering strategy +- [ ] Delete `functions/igraph/cluster.R` + +--- + +## Phase 6 — Backend: Topology Metrics + +- [ ] Implement `DegreeStrategy` — port `igraph::degree()` +- [ ] Implement `BetweennessStrategy` — port `igraph::betweenness()` +- [ ] Implement `ClusteringCoefficientStrategy` — port `igraph::transitivity()` +- [ ] Implement `POST /api/topology` router +- [ ] Write pytest tests for each metric with known fixture graphs +- [ ] Delete `functions/igraph/topology.R` + +--- + +## Phase 7 — Backend: Session & External API + +- [ ] Implement Pydantic models for session (`models/session.py`) +- [ ] Implement `POST /api/session/import` router — port `importNetwork()` +- [ ] Implement `POST /api/session/export` router — port `convertSessionToJSON()` +- [ ] Implement `POST /api/external` + `GET /api/external/` — port `resolveAPI()`; token = base64-encoded compressed session JSON +- [ ] Write pytest tests for all session and external endpoints using `www/data/*.json` as fixtures +- [ ] Delete `functions/init.R`, `functions/general.R`, `functions/reset.R`, `functions/vr.R`, `functions/edges.R`, `functions/render.R`, `functions/js_handling.R` + +--- + +## Phase 8 — Frontend: Foundation + +- [ ] Install Three.js via npm, configure `@types/three` +- [ ] Implement `EventBus` singleton (`src/bus/index.ts`) +- [ ] Implement `AppState` store singleton (`src/store/index.ts`) +- [ ] Implement `Command` interface + `CommandHistory` singleton (`src/commands/base.ts`) +- [ ] Generate typed API client from FastAPI OpenAPI spec (`src/api/client.ts`) +- [ ] Verify API client can call all Phase 2–7 endpoints + +--- + +## Phase 9 — Frontend: Three.js Classes + +- [ ] Identify all manual patches in `www/js/three/three.js` — document each one +- [ ] Migrate `Scene.js` → `Scene.ts` — reimplement patches as subclasses in `src/three/` +- [ ] Migrate `Layer.js` → `Layer.ts` +- [ ] Migrate `Node.js` → `Node.ts` +- [ ] Migrate `Edge.js` → `Edge.ts` +- [ ] Write Vitest unit tests for all four classes +- [ ] Delete `www/js/classes/`, `www/js/three/` + +--- + +## Phase 10 — Frontend: Commands + +- [ ] Implement `LoadNetworkCommand` +- [ ] Implement `ApplyLayoutCommand` — captures node positions before/after +- [ ] Implement `ApplyClusteringCommand` — captures node colors/cluster IDs before/after +- [ ] Implement `ApplyTopologyCommand` — captures node scale values before/after +- [ ] Implement `MoveLayerCommand` — captures layer transform before/after +- [ ] Implement `ChangeNodeColorCommand` +- [ ] Implement `ChangeNodeSizeCommand` +- [ ] Implement `ChangeEdgeColorCommand` +- [ ] Implement `ChangeThemeCommand` +- [ ] Write Vitest tests for execute/undo/redo on each command + +--- + +## Phase 11 — Frontend: Object Actions + +- [ ] Migrate `canvas_controls.js` → `src/actions/canvas_controls.ts` +- [ ] Migrate `layout.js` → `src/actions/layout.ts` +- [ ] Migrate `node.js` → `src/actions/node.ts` +- [ ] Migrate `edge.js` → `src/actions/edge.ts` +- [ ] Migrate `layer.js` → `src/actions/layer.ts` +- [ ] Migrate `network.js` → `src/actions/network.ts` +- [ ] Migrate `screen.js` → `src/actions/screen.ts` +- [ ] Migrate `themes.js` → `src/actions/themes.ts` +- [ ] Migrate `labels.js` → `src/actions/labels.ts` +- [ ] Migrate `right_click_menu.js` → `src/actions/right_click_menu.ts` +- [ ] Delete `www/js/object_actions/` + +--- + +## Phase 12 — Frontend: Event Handling & Main Loop + +- [ ] Migrate `general.js` → `src/utils.ts` +- [ ] Migrate `event_listeners.js` → `src/event_listeners.ts` — add `Ctrl+Z` / `Ctrl+Shift+Z` undo/redo +- [ ] Migrate `on_page_load.js` → `src/main.ts` — Three.js canvas setup, calls `GET /api/config` on load +- [ ] Replace `rshiny_handlers.js` + `rshiny_update.js` with typed EventBus + API client calls +- [ ] Delete `www/js/event_listeners.js`, `www/js/on_page_load.js`, `www/js/general.js` +- [ ] Delete `www/js/rshiny_handlers.js`, `www/js/rshiny_update.js` +- [ ] Delete `www/js/config/` + +--- + +## Phase 13 — Frontend: UI Panels + +- [ ] Build `frontend/index.html` — Bootstrap 5 navbar structure matching current tab layout +- [ ] Migrate Home panel (`views/home.R` → `src/ui/home.ts`) +- [ ] Migrate File panel (`views/file.R` → `src/ui/file.ts`) +- [ ] Migrate Layer Selection & Layouts panel (`views/layouts.R` → `src/ui/layouts.ts`) +- [ ] Migrate Scene Actions panel (`views/scene.R` → `src/ui/scene.ts`) +- [ ] Migrate Layer Actions panel (`views/layer.R` → `src/ui/layer.ts`) +- [ ] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) +- [ ] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) +- [ ] Migrate View Data panel (`views/data.R` → `src/ui/data.ts`) +- [ ] Migrate Help panel (`views/help.R` → `src/ui/help.ts`) +- [ ] Add Undo/Redo buttons wired to `CommandHistory` +- [ ] Delete `views/`, `ui.R`, `server.R`, `global.R`, `www/arena3dweb.css` + +--- + +## Phase 14 — Final Cleanup & Production + +- [ ] Delete remaining `www/`, `functions/`, `config/` (R) directories +- [ ] Delete `Arena3Dweb.Rproj`, `Rprofile.site`, `global.R` +- [ ] Full Docker production build test — `docker build` + `docker run` +- [ ] Full Playwright E2E test suite pass — upload fixture network, apply layout, clustering, export session +- [ ] Update `CLAUDE.md` for new stack +- [ ] Update `README.md` for new stack and Docker instructions +- [ ] Swap `v3` → `main` +- [ ] Update Docker Hub build to point to new `main` diff --git a/SPEC.md b/SPEC.md new file mode 100644 index 0000000..cb3ef7f --- /dev/null +++ b/SPEC.md @@ -0,0 +1,418 @@ +# Arena3Dweb v3 — Restack Design Spec + +**Date:** 2026-04-06 +**Status:** Approved for implementation planning + +--- + +## 1. Context & Motivation + +Arena3Dweb is a 3D multilayered network visualization tool built with R/Shiny + Three.js. The JavaScript frontend (~4,300 lines of OOP Three.js code) already carries the majority of the application logic. R/Shiny acts as a server that parses files, runs graph algorithms, and bridges the UI. + +Key pain points driving the restack: + +- `www/js/three/three.js` (51,000 lines) is a manually patched copy of Three.js. The project-specific changes cannot be separated from the library, making upgrades impossible. +- Shiny's reactive/observer model is not designed for building a rich public REST API, which is a stated goal. +- R is a high barrier for contributors unfamiliar with it; the primary maintenance burden falls on one person with occasional master's students. +- Shiny adds latency through its WebSocket reactive model; a stateless REST backend is faster and horizontally scalable. + +Primary users are end users on the hosted version (arena3d.org). The deployment stack is invisible to them. The GitHub repository must be preserved — it is cited in two published papers. + +--- + +## 2. Chosen Stack + +| Layer | Technology | Replaces | +|---|---|---| +| Frontend build | Vite | Manual script tags in `ui.R` | +| Frontend language | TypeScript | Plain JavaScript | +| 3D engine | Three.js (npm) | Manually patched `three.js` copy | +| UI styling | Bootstrap 5 | Shiny/shinythemes (Darkly) | +| Backend | FastAPI (Python) | R/Shiny | +| Graph algorithms | NetworkX + scipy + python-louvain | igraph (R) | +| API contract | OpenAPI (auto-generated by FastAPI) | None | +| Container | Docker (slim Python base + nginx) | r-base Docker image | + +### Why TypeScript over JavaScript + +The existing frontend is already OOP (`Scene`, `Layer`, `Node`, `Edge` classes with complex state). TypeScript adds type safety to structure that already exists, provides full Three.js autocomplete via `@types/three`, and makes the codebase self-documenting for contributors. Vite handles TypeScript compilation transparently — no extra tooling overhead. + +### Why FastAPI over Node.js + +NetworkX mirrors igraph's API closely, making the algorithm port mechanical. Python is the lingua franca in bioinformatics — student contributors almost certainly know it. FastAPI is designed for rich REST APIs with automatic OpenAPI documentation. Node.js graph algorithm libraries are significantly less mature than Python's ecosystem. + +--- + +## 3. Architecture + +The app splits into two independently deployable units communicating over HTTP: + +``` +┌─────────────────────────────────────────────┐ +│ Browser │ +│ Vite SPA (TypeScript + Three.js) │ +│ - 3D scene rendering │ +│ - UI control panels (Bootstrap 5) │ +│ - Typed API client (auto-generated) │ +└────────────────┬────────────────────────────┘ + │ REST / JSON +┌────────────────▼────────────────────────────┐ +│ FastAPI (Python) │ +│ - Network file parsing & validation │ +│ - Graph algorithms (NetworkX) │ +│ - Session import/export │ +│ - Public REST API (OpenAPI docs at /docs) │ +└─────────────────────────────────────────────┘ +``` + +**Stateless server.** The FastAPI backend holds zero per-user state between requests. The frontend (TypeScript state store) is the single source of truth. Every algorithm request carries its own complete graph payload; the server is a pure computation service. This means simultaneous users are naturally isolated — no session management, no memory leaks, and the backend is horizontally scalable behind a load balancer without additional infrastructure. + +**Serving in production.** nginx serves the static Vite build and proxies `/api/*` to uvicorn. One Docker image, one container. `docker run -p 8080:8080 arena3dweb` works identically to today. + +**No heavy UI framework.** The app is a Three.js canvas with control panels. Vanilla TypeScript for panel wiring, Bootstrap 5 for styling. Adding React or Vue on top of Three.js adds complexity without payoff. + +**Communication: REST only.** Every interaction is a typed `fetch()` call. FastAPI supports WebSockets natively if streaming progress on large graph computations is needed later — not a day-one requirement. + +--- + +## 4. Design Patterns + +### Frontend + +**Command Pattern + CommandHistory** — undo/redo for all scene mutations. + +Every user action that mutates scene state becomes a `Command` object capturing how to execute and reverse it. A `CommandHistory` singleton manages the stacks. + +```typescript +// src/commands/base.ts +interface Command { + execute(): void + undo(): void + description: string // shown in UI: "Undo: Apply Force Layout" +} + +class CommandHistory { + private undoStack: Command[] = [] + private redoStack: Command[] = [] + + execute(command: Command) { + command.execute() + this.undoStack.push(command) + this.redoStack = [] + } + undo() { + const cmd = this.undoStack.pop() + if (cmd) { cmd.undo(); this.redoStack.push(cmd) } + } + redo() { + const cmd = this.redoStack.pop() + if (cmd) { cmd.execute(); this.undoStack.push(cmd) } + } +} +``` + +Commands to implement: `ApplyLayoutCommand`, `ApplyClusteringCommand`, `ApplyTopologyCommand`, `MoveLayerCommand`, `ChangeNodeColorCommand`, `ChangeNodeSizeCommand`, `ChangeEdgeColorCommand`, `ChangeThemeCommand`, `LoadNetworkCommand`. + +File upload and session export are NOT commands — they are initialization/read-only events. + +Keyboard shortcuts: `Ctrl+Z` → `history.undo()`, `Ctrl+Shift+Z` → `history.redo()`. Undo/Redo buttons reflect `undoStack.length > 0` / `redoStack.length > 0`. + +--- + +**Event Bus** — decouples UI panels from the Three.js scene. + +```typescript +// src/bus/index.ts +bus.emit('node:color-changed', { nodeId: 'A', color: '#ff0000' }) +bus.on('node:color-changed', ({ nodeId, color }) => scene.updateNodeColor(nodeId, color)) +``` + +UI panels never import Three.js. The scene never imports UI code. Each side is independently testable. Replaces the role of `rshiny_handlers.js` / `rshiny_update.js`. + +--- + +**State Store (singleton)** — single typed source of truth, replaces scattered Shiny `input$` values and JS globals. + +```typescript +// src/store/index.ts +interface AppState { + config: AppConfig | null + network: NetworkData | null + selectedLayers: string[] + selectedNodes: string[] + selectedChannels: string[] + currentTheme: Theme +} +``` + +UI panels read from the store. Commands update it. The event bus notifies the scene. + +### Backend + +**Strategy Pattern** — plug-in layout, clustering, and topology algorithms. + +```python +# app/services/algorithms/base.py +class LayoutStrategy(ABC): + @abstractmethod + def compute(self, graph: nx.Graph, **kwargs) -> dict[str, list[float]]: ... + +# app/services/algorithms/registry.py +LAYOUTS: dict[str, LayoutStrategy] = { + "force": ForceDirectedLayout(), + "circle": CircularLayout(), + "grid": GridLayout(), + "random": RandomLayout(), +} +``` + +Adding a new algorithm = add one file + one line in the registry. The router never changes. Each strategy is independently unit-testable. + +**Service Layer** — thin routers, all business logic in services. + +``` +routers/layout.py → validates request, calls services/graph.py +services/graph.py → builds nx.Graph, calls algorithm strategy +services/parser.py → TSV/JSON parsing and validation +``` + +--- + +## 5. API Contract + +### Full API surface + +| Method | Endpoint | Replaces | +|---|---|---| +| `GET` | `/api/config` | `handler_initializeGlobals` | +| `POST` | `/api/network` | `handleUploadNetwork()` | +| `POST` | `/api/layout` | `functions/igraph/layout.R` | +| `POST` | `/api/cluster` | `functions/igraph/cluster.R` | +| `POST` | `/api/topology` | `functions/igraph/topology.R` | +| `POST` | `/api/session/import` | `importNetwork()` | +| `POST` | `/api/session/export` | `convertSessionToJSON()` | +| `POST` | `/api/external` | `resolveAPI()` | +| `GET` | `/api/external/` | *(new)* | + +OpenAPI docs auto-generated at `/docs`. + +### Key data flows + +**App initialisation** +``` +GET /api/config +← { max_layers, max_edges, max_channels, edge_default_color, channel_colors } +→ Store.update(config) +→ Three.js scene constructed +``` + +**Network upload** +``` +POST /api/network { file: } +← { nodes: [{id, label, layer},...], edges: [{src, trg, weight?, channel?, color?},...], + layers: [...], warnings: [] } +→ history.execute(new LoadNetworkCommand(response)) +``` + +**Layout request** +``` +POST /api/layout { nodes, edges, algorithm, selected_layers, options } +← { positions: { "NodeA::LayerA": [x, y, z], ... } } +→ history.execute(new ApplyLayoutCommand(response.positions)) +``` + +**Clustering** +``` +POST /api/cluster { nodes, edges, algorithm, selected_layers } +← { clusters: { "NodeA::LayerA": 0, ... }, cluster_count: 4 } +→ history.execute(new ApplyClusteringCommand(response.clusters)) +``` + +**Topology metrics** +``` +POST /api/topology { nodes, edges, metric, selected_layers } +← { values: { "NodeA::LayerA": 0.82, ... }, min: 0.0, max: 1.0 } +→ history.execute(new ApplyTopologyCommand(response.values)) +``` + +**Session export** +``` +POST /api/session/export { layers, nodes, edges, scene } +← application/json download +(no Command — read-only) +``` + +**Session import** +``` +POST /api/session/import { file: } +← { layers, nodes, edges, scene, direction, ... } +→ history.execute(new LoadNetworkCommand(response)) +``` + +**External API** +``` +POST /api/external { nodes, edges, metadata? } +← { url: "https://arena3d.org/?session=" } + +GET /api/external/ +← full session JSON → same as session import flow +``` + +The token is a base64-encoded compressed representation of the session JSON — no server-side storage required, keeping the stateless model intact. The URL is self-contained. + +### igraph → NetworkX mapping + +| R/igraph | Python/NetworkX | +|---|---| +| `graph_from_data_frame()` | `nx.from_pandas_edgelist()` | +| `layout_with_fr()` | `nx.spring_layout()` | +| `layout_with_kk()` | `nx.kamada_kawai_layout()` | +| `layout_in_circle()` | `nx.circular_layout()` | +| `cluster_louvain()` | `community.best_partition()` (python-louvain) | +| `cluster_walktrap()` | `nx.community.asyn_lpa_communities()` (LPA — closest available; exact walktrap port via `igraph` Python bindings if needed) | +| `betweenness()` | `nx.betweenness_centrality()` | +| `transitivity()` | `nx.clustering()` | +| `degree()` | `nx.degree_centrality()` | + +--- + +## 6. Directory Structure + +``` +Arena3Dweb/ ← same GitHub repo, v3 branch + frontend/ + src/ + classes/ # Scene.ts, Layer.ts, Node.ts, Edge.ts + actions/ # canvas_controls.ts, layout.ts, node.ts, edge.ts … + commands/ # base.ts + one file per command + config/ # static_variables.ts, global_variables.ts + api/ # typed client generated from OpenAPI spec + store/ # AppState singleton + bus/ # EventBus singleton + three/ # Three.js subclasses/extensions only + ui/ # tab panel wiring + main.ts + index.html + vite.config.ts # /api proxied to localhost:8000 in dev + tsconfig.json + package.json + .eslintrc.json + .prettierrc + backend/ + app/ + main.py + config.py # mirrors server_variables.R constants + models/ # Pydantic schemas: network, layout, cluster, topology, session + routers/ # one file per endpoint group + services/ + parser.py # ports functions/input.R + graph.py # nx.Graph construction helpers + algorithms/ + base.py # abstract Strategy base classes + layouts/ # force.py, circle.py, grid.py, random.py + clusters/ # louvain.py, walktrap.py + topology/ # degree.py, betweenness.py, clustering_coeff.py + tests/ + fixtures/ # reuse www/data/*.json and TSV files directly + test_network.py + test_layout.py + test_cluster.py + test_topology.py + test_session.py + test_external.py + pyproject.toml # Ruff + mypy config + requirements.txt + docs/ + superpowers/specs/ + nginx/ + nginx.conf + .github/ + workflows/ + ci.yml # lint → typecheck → unit tests → E2E + docker-compose.yml # dev: hot-reload frontend + backend + Dockerfile # prod: single image, nginx + uvicorn + MIGRATION.md # checklist: old file → new equivalent, checked off on deletion + CLAUDE.md +``` + +--- + +## 7. Tooling + +### Python (backend) + +| Purpose | Tool | +|---|---| +| Lint + format | Ruff (configured in `pyproject.toml`) | +| Type checking | mypy | +| Testing | pytest + httpx (async FastAPI client) | + +### TypeScript (frontend) + +| Purpose | Tool | +|---|---| +| Lint | ESLint + `@typescript-eslint` | +| Format | Prettier | +| Unit testing | Vitest | +| E2E testing | Playwright | + +### Repo-level + +| Purpose | Tool | +|---|---| +| Pre-commit hooks | pre-commit (runs Ruff + ESLint/Prettier before each commit) | +| CI | GitHub Actions: lint → typecheck → unit tests → E2E | + +--- + +## 8. Repository & Migration Strategy + +### Branching + +``` +main ── R/Shiny version stays live throughout ──────────────────► + │ + └── tag: v2-legacy +v3 ── new stack developed here ───────────────────────────────► + │ + └── swap to main when ready +``` + +Docker Hub continues building from `main` until `v3` is production-ready. The `v2-legacy` tag preserves the R/Shiny version permanently for reproducibility. + +### Migration mechanic: port-and-verify + +The `v3` branch starts as a copy of `main`. New `frontend/` and `backend/` directories are scaffolded. Old R/Shiny files remain as the **living specification** — read the old file, port it, test it, delete it. + +**Deletion = done signal.** At any point, the presence of old files shows exactly what remains to be migrated. `MIGRATION.md` tracks the checklist explicitly. + +Example sequence: +``` +1. Scaffold backend + frontend (no old files deleted yet) +2. Port + test POST /api/config → delete config/server_variables.R +3. Port + test POST /api/network → delete functions/input.R +4. Port + test POST /api/layout → delete functions/igraph/layout.R +5. Port + test POST /api/cluster → delete functions/igraph/cluster.R +6. Port + test POST /api/topology → delete functions/igraph/topology.R +7. Port + test session import/export → delete functions/init.R, functions/general.R +8. Migrate Three.js classes → TypeScript → delete www/js/classes/ +9. Migrate object actions → TypeScript → delete www/js/object_actions/ +10. Migrate event handling + main loop → delete www/js/event_listeners.js, on_page_load.js +11. Migrate UI panels → index.html + TS → delete views/, ui.R, server.R, global.R +12. Final cleanup → delete www/, functions/, views/, config/ (R) +``` + +Each step is independently runnable and testable before the next begins. + +### Development workflow + +```bash +# backend — hot reload +cd backend && uvicorn app.main:app --reload + +# frontend — hot reload, /api proxied to backend +cd frontend && npm run dev + +# both together +docker-compose up +``` From c64a68ded3966a76b29055af1095590dc1312de0 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Mon, 6 Apr 2026 10:25:20 +0100 Subject: [PATCH 002/166] scaffolding front- and backend folder structure + config files init, init status check for FastAPI and frontend run dev connecting to it --- .gitignore | 14 + CLAUDE.md | 49 +- PLAN.md | 18 +- backend/app/__init__.py | 0 backend/app/config.py | 1 + backend/app/main.py | 8 + backend/app/models/__init__.py | 0 backend/app/routers/__init__.py | 0 backend/app/services/__init__.py | 0 backend/app/services/algorithms/__init__.py | 0 .../services/algorithms/clusters/__init__.py | 0 .../services/algorithms/layouts/__init__.py | 0 .../services/algorithms/topology/__init__.py | 0 backend/pyproject.toml | 36 + backend/requirements.txt | 12 + backend/tests/__init__.py | 0 backend/tests/fixtures/.gitkeep | 0 frontend/.prettierrc | 6 + frontend/eslint.config.js | 16 + frontend/index.html | 12 + frontend/package-lock.json | 4104 +++++++++++++++++ frontend/package.json | 28 + frontend/src/actions/.gitkeep | 0 frontend/src/api/.gitkeep | 0 frontend/src/bus/.gitkeep | 0 frontend/src/classes/.gitkeep | 0 frontend/src/commands/.gitkeep | 0 frontend/src/config/.gitkeep | 0 frontend/src/main.ts | 2 + frontend/src/store/.gitkeep | 0 frontend/src/three/.gitkeep | 0 frontend/src/ui/.gitkeep | 0 frontend/tsconfig.json | 14 + frontend/vite.config.ts | 9 + 34 files changed, 4315 insertions(+), 14 deletions(-) create mode 100644 backend/app/__init__.py create mode 100644 backend/app/config.py create mode 100644 backend/app/main.py create mode 100644 backend/app/models/__init__.py create mode 100644 backend/app/routers/__init__.py create mode 100644 backend/app/services/__init__.py create mode 100644 backend/app/services/algorithms/__init__.py create mode 100644 backend/app/services/algorithms/clusters/__init__.py create mode 100644 backend/app/services/algorithms/layouts/__init__.py create mode 100644 backend/app/services/algorithms/topology/__init__.py create mode 100644 backend/pyproject.toml create mode 100644 backend/requirements.txt create mode 100644 backend/tests/__init__.py create mode 100644 backend/tests/fixtures/.gitkeep create mode 100644 frontend/.prettierrc create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/src/actions/.gitkeep create mode 100644 frontend/src/api/.gitkeep create mode 100644 frontend/src/bus/.gitkeep create mode 100644 frontend/src/classes/.gitkeep create mode 100644 frontend/src/commands/.gitkeep create mode 100644 frontend/src/config/.gitkeep create mode 100644 frontend/src/main.ts create mode 100644 frontend/src/store/.gitkeep create mode 100644 frontend/src/three/.gitkeep create mode 100644 frontend/src/ui/.gitkeep create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts diff --git a/.gitignore b/.gitignore index ba31e72..02f279b 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,17 @@ vignettes/*.pdf # R Environment Variables .Renviron Arena3Dweb.Rproj + +# Python +backend/.venv/ +__pycache__/ +*.pyc +.mypy_cache/ +.ruff_cache/ + +# Node +frontend/node_modules/ +frontend/dist/ + +# Editors +.DS_Store diff --git a/CLAUDE.md b/CLAUDE.md index df93f6a..357a392 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,6 +14,50 @@ If you are on the `v3` branch, the presence of old R/Shiny files (`server.R`, `f ## Running the App +### v3 (FastAPI + Vite — active development, `v3` branch) + +**Backend:** +```bash +cd backend +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +uvicorn app.main:app --reload # http://localhost:8000 +``` + +**Frontend:** +```bash +cd frontend +npm install +npm run dev # http://localhost:5173 — /api proxied to localhost:8000 +``` + +**Both together (Docker):** +```bash +docker-compose up +``` + +**Backend tests:** +```bash +cd backend && pytest +``` + +**Frontend tests:** +```bash +cd frontend && npm test # Vitest unit tests +cd frontend && npm run test:e2e # Playwright E2E +``` + +**Lint / format:** +```bash +cd backend && ruff check . && ruff format . +cd frontend && npm run lint && npm run format +``` + +--- + +### v2 (R/Shiny — legacy, `main` branch) + **From RStudio:** 1. Open `Arena3Dweb.Rproj` 2. Open `server.R`, select "Run External", click "Run App" @@ -29,11 +73,6 @@ docker run -p 3838:3838 pavlopouloslab/arena3dweb shiny::runApp('.') ``` -**Required R packages:** -```r -install.packages(c("shiny", "shinyjs", "shinythemes", "igraph", "RColorBrewer", "jsonlite", "tidyr", "dplyr", "DT", "fst")) -``` - ## Architecture Overview This is an **R/Shiny + Three.js** web application for interactive 3D visualization of multilayered networks. diff --git a/PLAN.md b/PLAN.md index 7673e8e..5b1e920 100644 --- a/PLAN.md +++ b/PLAN.md @@ -11,20 +11,20 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. ## Phase 1 — Scaffolding & Tooling -- [ ] Create `v3` branch from `main` -- [ ] Tag current `main` as `v2-legacy` -- [ ] Scaffold `backend/` directory structure -- [ ] Scaffold `frontend/` directory structure -- [ ] Set up `backend/pyproject.toml` — Ruff + mypy config -- [ ] Set up `frontend/package.json` — Vite + TypeScript + ESLint + Prettier + Vitest -- [ ] Set up `frontend/vite.config.ts` — proxy `/api` to `localhost:8000` in dev -- [ ] Set up `frontend/tsconfig.json` +- [x] Create `v3` branch from `main` +- [x] Tag current `main` as `v2-legacy` +- [x] Scaffold `backend/` directory structure +- [x] Scaffold `frontend/` directory structure +- [x] Set up `backend/pyproject.toml` — Ruff + mypy config +- [x] Set up `frontend/package.json` — Vite + TypeScript + ESLint + Prettier + Vitest +- [x] Set up `frontend/vite.config.ts` — proxy `/api` to `localhost:8000` in dev +- [x] Set up `frontend/tsconfig.json` - [ ] Set up `docker-compose.yml` — hot-reload frontend + backend - [ ] Set up `Dockerfile` — production single image (nginx + uvicorn) - [ ] Set up `nginx/nginx.conf` — serve static build, proxy `/api` - [ ] Set up `.github/workflows/ci.yml` — lint → typecheck → unit tests → E2E - [ ] Set up `.pre-commit-config.yaml` — Ruff + ESLint/Prettier hooks -- [ ] Create `MIGRATION.md` with full old-file → new-file checklist +- [x] Create `MIGRATION.md` with full old-file → new-file checklist --- diff --git a/backend/app/__init__.py b/backend/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/config.py b/backend/app/config.py new file mode 100644 index 0000000..d40a5be --- /dev/null +++ b/backend/app/config.py @@ -0,0 +1 @@ +# Application constants — ports config/server_variables.R (Phase 2) diff --git a/backend/app/main.py b/backend/app/main.py new file mode 100644 index 0000000..cad6aa8 --- /dev/null +++ b/backend/app/main.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI + +app = FastAPI(title="Arena3Dweb API", version="3.0.0") + + +@app.get("/api/health") +async def health() -> dict[str, str]: + return {"status": "ok"} diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/routers/__init__.py b/backend/app/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/services/__init__.py b/backend/app/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/services/algorithms/__init__.py b/backend/app/services/algorithms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/services/algorithms/clusters/__init__.py b/backend/app/services/algorithms/clusters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/services/algorithms/layouts/__init__.py b/backend/app/services/algorithms/layouts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/services/algorithms/topology/__init__.py b/backend/app/services/algorithms/topology/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/pyproject.toml b/backend/pyproject.toml new file mode 100644 index 0000000..8f4b2e0 --- /dev/null +++ b/backend/pyproject.toml @@ -0,0 +1,36 @@ +[project] +name = "arena3dweb" +version = "3.0.0" +requires-python = ">=3.11" +dependencies = [ + "fastapi>=0.115.0", + "uvicorn[standard]>=0.30.0", + "networkx>=3.3", + "scipy>=1.14.0", + "python-louvain>=0.16", + "pandas>=2.2.0", + "pydantic>=2.8.0", +] + +[project.optional-dependencies] +dev = [ + "httpx>=0.27.0", + "pytest>=8.3.0", + "pytest-asyncio>=0.24.0", + "mypy>=1.11.0", + "ruff>=0.6.0", +] + +[tool.ruff] +line-length = 100 +target-version = "py311" + +[tool.ruff.lint] +select = ["E", "F", "I", "UP", "B"] + +[tool.mypy] +python_version = "3.11" +strict = true + +[tool.pytest.ini_options] +asyncio_mode = "auto" diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..e9a9ded --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,12 @@ +fastapi>=0.115.0 +uvicorn[standard]>=0.30.0 +networkx>=3.3 +scipy>=1.14.0 +python-louvain>=0.16 +pandas>=2.2.0 +pydantic>=2.8.0 +httpx>=0.27.0 +pytest>=8.3.0 +pytest-asyncio>=0.24.0 +mypy>=1.11.0 +ruff>=0.6.0 diff --git a/backend/tests/__init__.py b/backend/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/tests/fixtures/.gitkeep b/backend/tests/fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..ad4d895 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5" +} diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..3d53844 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,16 @@ +import tseslint from 'typescript-eslint' + +export default tseslint.config( + ...tseslint.configs.recommendedTypeChecked, + { + languageOptions: { + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + ignores: ['dist/**'], + } +) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..0a91271 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + Arena3Dweb + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..ac6b1dd --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,4104 @@ +{ + "name": "arena3dweb", + "version": "3.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "arena3dweb", + "version": "3.0.0", + "dependencies": { + "bootstrap": "^5.3.3", + "three": "^0.170.0" + }, + "devDependencies": { + "@playwright/test": "^1.48.0", + "@types/three": "^0.170.0", + "eslint": "^9.0.0", + "prettier": "^3.0.0", + "typescript": "^5.6.0", + "typescript-eslint": "^8.0.0", + "vite": "^6.0.0", + "vitest": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.170.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.170.0.tgz", + "integrity": "sha512-CUm2uckq+zkCY7ZbFpviRttY+6f9fvwm6YqSqPfA5K22s9w7R4VnA3rzJse8kHVvuzLcTx+CjNCs2NYe0QFAyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": "*", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.18.1" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@webgpu/types": { + "version": "0.1.69", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", + "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/three": { + "version": "0.170.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz", + "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==", + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vite-node/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vite-node/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..9f6f6fc --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,28 @@ +{ + "name": "arena3dweb", + "version": "3.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", + "lint": "eslint src", + "format": "prettier --write src", + "test": "vitest", + "test:e2e": "playwright test" + }, + "dependencies": { + "bootstrap": "^5.3.3", + "three": "^0.170.0" + }, + "devDependencies": { + "@playwright/test": "^1.48.0", + "@types/three": "^0.170.0", + "eslint": "^9.0.0", + "prettier": "^3.0.0", + "typescript": "^5.6.0", + "typescript-eslint": "^8.0.0", + "vite": "^6.0.0", + "vitest": "^2.0.0" + } +} diff --git a/frontend/src/actions/.gitkeep b/frontend/src/actions/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/api/.gitkeep b/frontend/src/api/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/bus/.gitkeep b/frontend/src/bus/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/classes/.gitkeep b/frontend/src/classes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/commands/.gitkeep b/frontend/src/commands/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/config/.gitkeep b/frontend/src/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..814e93d --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,2 @@ +// Entry point — Three.js canvas setup and app initialisation +// Ports: www/js/on_page_load.js (Phase 12) diff --git a/frontend/src/store/.gitkeep b/frontend/src/store/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/three/.gitkeep b/frontend/src/three/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/ui/.gitkeep b/frontend/src/ui/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..44704ff --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "sourceMap": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"] + }, + "include": ["src", "vite.config.ts", "eslint.config.js"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..a57b0de --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + server: { + proxy: { + '/api': 'http://localhost:8000', + }, + }, +}) From f5158c46fceece09b180c59359a0dc0b55b87df6 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 19:28:08 +0100 Subject: [PATCH 003/166] plan, spec and migration docs revised and updated with Fable 5, serena init --- .serena/.gitignore | 2 + .serena/project.yml | 133 ++++++++++++++++++++++++++++++++++++++++++++ MIGRATION.md | 36 ++++++------ PLAN.md | 54 +++++++++--------- SPEC.md | 124 +++++++++++++++++++++++------------------ 5 files changed, 252 insertions(+), 97 deletions(-) create mode 100644 .serena/.gitignore create mode 100644 .serena/project.yml diff --git a/.serena/.gitignore b/.serena/.gitignore new file mode 100644 index 0000000..2e510af --- /dev/null +++ b/.serena/.gitignore @@ -0,0 +1,2 @@ +/cache +/project.local.yml diff --git a/.serena/project.yml b/.serena/project.yml new file mode 100644 index 0000000..5f746ff --- /dev/null +++ b/.serena/project.yml @@ -0,0 +1,133 @@ +# the name by which the project can be referenced within Serena +project_name: "Arena3Dweb" + + +# list of languages for which language servers are started; choose from: +# al angular ansible bash clojure +# cpp cpp_ccls crystal csharp csharp_omnisharp +# dart elixir elm erlang fortran +# fsharp go groovy haskell haxe +# hlsl html java json julia +# kotlin lean4 lua luau markdown +# matlab msl nix ocaml pascal +# perl php php_phpactor powershell python +# python_jedi python_ty r rego ruby +# ruby_solargraph rust scala scss solidity +# svelte swift systemverilog terraform toml +# typescript typescript_vts vue yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Angular projects, use angular (subsumes typescript+html; requires `npm install` in the project root) +# - For Svelte projects, use svelte (subsumes typescript/javascript for .svelte projects; requires npm) +# - For SCSS / Sass / plain CSS, use scss (some-sass-language-server handles all three) +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: +- r + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: "utf-8" + +# line ending convention to use when writing source files. +# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) +# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. +line_ending: + +# The language backend to use for this project. +# If not set, the global setting from serena_config.yml is used. +# Valid values: LSP, JetBrains +# Note: the backend is fixed at startup. If a project with a different backend +# is activated post-init, an error will be returned. +language_backend: + +# whether to use project's .gitignore files to ignore files +ignore_all_files_in_gitignore: true + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} + +# list of additional workspace folder paths for cross-package reference support (e.g. in monorepos). +# Paths can be absolute or relative to the project root. +# Each folder is registered as an LSP workspace folder, enabling language servers to discover +# symbols and references across package boundaries. +# Currently supported for: TypeScript. +# Example: +# additional_workspace_folders: +# - ../sibling-package +# - ../shared-lib +additional_workspace_folders: [] + +# list of additional paths to ignore in this project. +# Same syntax as gitignore, so you can use * and **. +# Note: global ignored_paths from serena_config.yml are also applied additively. +ignored_paths: [] + +# whether the project is in read-only mode +# If set to true, all editing tools will be disabled and attempts to use them will result in an error +# Added on 2025-04-18 +read_only: false + +# list of tool names to exclude. +# This extends the existing exclusions (e.g. from the global configuration) +# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html +excluded_tools: [] + +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default). +# This extends the existing inclusions (e.g. from the global configuration). +# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html +included_optional_tools: [] + +# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. +# This cannot be combined with non-empty excluded_tools or included_optional_tools. +# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html +fixed_tools: [] + +# list of mode names that are to be activated by default, overriding the setting in the global configuration. +# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes. +# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this overrides the setting from the global configuration (serena_config.yml). +# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply +# for this project. +# This setting can, in turn, be overridden by CLI parameters (--mode). +# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes +default_modes: + +# list of mode names to be activated additionally for this project, e.g. ["query-projects"] +# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes. +# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes +added_modes: + +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: "" + +# time budget (seconds) per tool call for the retrieval of additional symbol information +# such as docstrings or parameter information. +# This overrides the corresponding setting in the global configuration; see the documentation there. +# If null or missing, use the setting from the global configuration. +symbol_info_budget: + +# list of regex patterns which, when matched, mark a memory entry as read‑only. +# Extends the list from the global configuration, merging the two lists. +read_only_memory_patterns: [] + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] diff --git a/MIGRATION.md b/MIGRATION.md index 1c3f42f..68208cd 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -14,19 +14,19 @@ See `PLAN.md` for the ordered implementation steps. | `config/server_variables.R` | `backend/app/config.py` | [ ] | | `config/global_variables.R` | `backend/app/config.py` | [ ] | | `config/static_variables.R` | `backend/app/config.py` | [ ] | -| `config/ui_variables.R` | *(absorbed into frontend config)* | [ ] | +| `config/ui_variables.R` | *(absorbed into frontend config — see Removed)* | [ ] | | `functions/input.R` | `backend/app/services/parser.py` + `backend/app/routers/network.py` | [ ] | | `functions/init.R` | `backend/app/routers/config.py` + `backend/app/main.py` | [ ] | -| `functions/general.R` | `backend/app/services/graph.py` | [ ] | +| `functions/general.R` | `backend/app/services/parser.py` (file read) + `backend/app/services/topology.py` (`mapper()`) — JS-bridge helpers die with Shiny | [ ] | | `functions/reset.R` | *(stateless server — no equivalent needed)* | [ ] | | `functions/render.R` | *(absorbed into FastAPI error responses)* | [ ] | | `functions/js_handling.R` | *(absorbed into frontend EventBus)* | [ ] | -| `functions/edges.R` | `backend/app/routers/network.py` | [ ] | -| `functions/vr.R` | `backend/app/routers/session.py` | [ ] | +| `functions/edges.R` | `frontend/src/ui/edge.ts` — UI visibility toggles + JS handler calls, no server logic | [ ] | +| `functions/vr.R` | `backend/app/routers/vr.py` — PLY + A-Frame HTML generation *(keep/drop decision, PLAN Phase 7)* | [ ] | | `functions/igraph/general.R` | `backend/app/services/graph.py` | [ ] | -| `functions/igraph/layout.R` | `backend/app/services/algorithms/layouts/` | [ ] | -| `functions/igraph/cluster.R` | `backend/app/services/algorithms/clusters/` | [ ] | -| `functions/igraph/topology.R` | `backend/app/services/algorithms/topology/` | [ ] | +| `functions/igraph/layout.R` | `backend/app/services/layouts.py` | [ ] | +| `functions/igraph/cluster.R` | `backend/app/services/clustering.py` — folded into `POST /api/layout` as optional step | [ ] | +| `functions/igraph/topology.R` | `backend/app/services/topology.py` | [ ] | --- @@ -98,13 +98,15 @@ See `PLAN.md` for the ordered implementation steps. ## Algorithm Notes -| Algorithm | Old (igraph) | New (NetworkX) | Exact match? | -|---|---|---|---| -| Force-directed layout | `layout_with_fr()` | `nx.spring_layout()` | Approximate | -| Circular layout | `layout_in_circle()` | `nx.circular_layout()` | Yes | -| Kamada-Kawai layout | `layout_with_kk()` | `nx.kamada_kawai_layout()` | Yes | -| Louvain clustering | `cluster_louvain()` | `community.best_partition()` | Yes | -| Walktrap clustering | `cluster_walktrap()` | `nx.community.asyn_lpa_communities()` | No — LPA approximation; verify output matches or use `igraph` Python bindings | -| Degree | `degree()` | `nx.degree_centrality()` | Yes | -| Betweenness centrality | `betweenness()` | `nx.betweenness_centrality()` | Yes | -| Clustering coefficient | `transitivity()` | `nx.clustering()` | Yes | +Backend uses **python-igraph** — same C core as R's igraph, so all 11 layouts, 4 clustering algorithms, and 3 topology metrics port 1:1 with identical output (full mapping table in SPEC §5). Non-obvious renames and v2 parameters to preserve: + +| v2 behaviour | Port note | +|---|---| +| `cluster_louvain()` | `Graph.community_multilevel()` (igraph's name for Louvain) | +| `layout_as_tree()` ("Reingold-Tilford") | `Graph.layout_reingold_tilford()` | +| `degree(mode = "all", loops = T, normalized = F)` | Raw `Graph.degree()` — **not** degree centrality | +| `transitivity(type = "weighted", isolates = "zero")` | `Graph.transitivity_local_undirected(weights=…)`, isolates → 0 | +| `betweenness(directed = input$edgeDirectionToggle, weights)` | Direction toggle + weights are request params | +| `set.seed(123)` before every layout | Seed igraph RNG per request — layouts must be reproducible | +| `NO_EDGE_LAYOUTS` (Circle, Grid, Random) | Run on pseudo-network linking isolated nodes with tiny weights (`filterPseudoNetwork()`) | +| Clustered layout | Supernode strategy in `cluster.R` (global layout on cluster graph + repelling force + local layout per cluster) — part of `/api/layout`, not a separate endpoint | diff --git a/PLAN.md b/PLAN.md index 5b1e920..4ea2635 100644 --- a/PLAN.md +++ b/PLAN.md @@ -30,11 +30,11 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. ## Phase 2 — Backend: Config Endpoint -- [ ] Implement `backend/app/config.py` — port constants from `config/server_variables.R` +- [ ] Implement `backend/app/config.py` — port constants from `config/server_variables.R`; backend keeps what it validates against (`MAX_EDGES`, `MAX_LAYERS`, `MAX_CHANNELS`, mandatory/optional column lists, scale targets), purely visual constants (node palette, channel colors, floor defaults) also served via `/api/config` for the frontend - [ ] Implement `GET /api/config` router - [ ] Write pytest tests for `/api/config` - [ ] Verify frontend can fetch and parse config response -- [ ] Delete `config/server_variables.R` +- [ ] Delete `config/server_variables.R` (`global_variables.R`, `static_variables.R`, `ui_variables.R` are 1–2 lines each — fold their constants in here too and delete them) --- @@ -51,35 +51,34 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. ## Phase 4 — Backend: Layout Algorithms -- [ ] Implement abstract `LayoutStrategy` base class (`services/algorithms/base.py`) -- [ ] Implement `ForceDirectedLayout` — port `igraph::layout_with_fr()` -- [ ] Implement `CircularLayout` — port `igraph::layout_in_circle()` -- [ ] Implement `GridLayout` -- [ ] Implement `RandomLayout` -- [ ] Implement algorithm registry (`services/algorithms/registry.py`) -- [ ] Implement `services/graph.py` — `nx.Graph` construction helpers (port `functions/igraph/general.R`) -- [ ] Implement `POST /api/layout` router -- [ ] Write pytest tests for each layout strategy with fixture graphs +- [ ] Swap `networkx` + `scipy` + `python-louvain` for `python-igraph` in `requirements.txt` (SPEC §2 — same C core as R igraph, all 11 UI layouts port exactly) +- [ ] Implement `services/graph.py` — `ig.Graph` construction (port `functions/igraph/general.R`: channel filter, perLayer/allLayers/nodesPerLayers subgraph scopes, `simplify()` multi-edge/loop rules) +- [ ] Implement `services/layouts.py` — registry dict mapping all 11 UI layout names to `Graph.layout_*` calls (port `getLayoutFunction()`) +- [ ] Port pseudo-network for no-edge layouts (`NO_EDGE_LAYOUTS` = Circle, Grid, Random) — `filterPseudoNetwork()` chains isolated nodes with tiny weights so whole layer participates +- [ ] Seed RNG per request (v2 uses `set.seed(123)`) — layouts must be reproducible +- [ ] Implement `POST /api/layout` router — scope + selected layers/nodes/channels params, returns 2D in-layer `[y, z]` coords (SPEC §5 flow) +- [ ] Write pytest tests for each layout with fixture graphs - [ ] Delete `functions/igraph/layout.R`, `functions/igraph/general.R` --- -## Phase 5 — Backend: Clustering Algorithms +## Phase 5 — Backend: Clustering (optional layout step) -- [ ] Implement `LouvainClustering` strategy — port `igraph::cluster_louvain()` -- [ ] Implement `WalktrapClustering` strategy — LPA approximation; note in MIGRATION.md if behaviour differs -- [ ] Implement `POST /api/cluster` router -- [ ] Write pytest tests for each clustering strategy +Clustering in v2 is not a standalone action — it is an option of the layout run (`calculateClusteredLayout()`): cluster, lay out cluster super-nodes globally, lay out members locally, translate into place. No separate `/api/cluster` endpoint. + +- [ ] Implement clustering registry (`services/clustering.py`) — Louvain, Walktrap, Fast Greedy, Label Propagation (`Graph.community_*`, all exact with python-igraph) +- [ ] Port supernode strategy (`execute_strategy3_superNodes_strictPartitioning`): global layout on cluster graph with repelling force, per-cluster local layout, coordinate translation +- [ ] Extend `POST /api/layout` with optional `clustering: { algorithm, local_layout }`; response gains `clusters` map (feeds Clustering Data table + node-color priority) +- [ ] Write pytest tests for each clustering algorithm and the clustered layout - [ ] Delete `functions/igraph/cluster.R` --- ## Phase 6 — Backend: Topology Metrics -- [ ] Implement `DegreeStrategy` — port `igraph::degree()` -- [ ] Implement `BetweennessStrategy` — port `igraph::betweenness()` -- [ ] Implement `ClusteringCoefficientStrategy` — port `igraph::transitivity()` -- [ ] Implement `POST /api/topology` router +- [ ] Implement `services/topology.py` — Degree (raw `Graph.degree()`, **not** normalized centrality), Clustering Coefficient (weighted local transitivity, isolates = 0), Betweenness (honours edge-direction toggle + weights) +- [ ] Port `mapper()` — map values into `[TARGET_NODE_SCALE_MIN, TARGET_NODE_SCALE_MAX]` server-side as v2 does; return raw values too for the View Data table +- [ ] Implement `POST /api/topology` router (same scope/filter params as layout) - [ ] Write pytest tests for each metric with known fixture graphs - [ ] Delete `functions/igraph/topology.R` @@ -90,9 +89,10 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. - [ ] Implement Pydantic models for session (`models/session.py`) - [ ] Implement `POST /api/session/import` router — port `importNetwork()` - [ ] Implement `POST /api/session/export` router — port `convertSessionToJSON()` -- [ ] Implement `POST /api/external` + `GET /api/external/` — port `resolveAPI()`; token = base64-encoded compressed session JSON +- [ ] Implement `POST /api/external` + `GET /api/external/` — port `resolveAPI()`; token = short random ID, session JSON stored in `tmp/` with TTL cleanup (base64-in-URL rejected — 10k-edge sessions exceed URL limits, see SPEC §5) +- [ ] Decide VR feature: port `functions/vr.R` → `routers/vr.py` (PLY + A-Frame HTML written to `tmp/`, served by `GET /api/vr/`) or drop with changelog note — v2 depends on external hosting at `bib.fleming.gr` - [ ] Write pytest tests for all session and external endpoints using `www/data/*.json` as fixtures -- [ ] Delete `functions/init.R`, `functions/general.R`, `functions/reset.R`, `functions/vr.R`, `functions/edges.R`, `functions/render.R`, `functions/js_handling.R` +- [ ] Delete `functions/init.R`, `functions/general.R`, `functions/reset.R`, `functions/vr.R`, `functions/render.R`, `functions/js_handling.R` (`functions/edges.R` is UI logic — dies with `views/` in Phase 13) --- @@ -122,8 +122,7 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. ## Phase 10 — Frontend: Commands - [ ] Implement `LoadNetworkCommand` -- [ ] Implement `ApplyLayoutCommand` — captures node positions before/after -- [ ] Implement `ApplyClusteringCommand` — captures node colors/cluster IDs before/after +- [ ] Implement `ApplyLayoutCommand` — captures node positions before/after, plus cluster IDs/colors when the layout ran with clustering (single command: one API response, one undo step) - [ ] Implement `ApplyTopologyCommand` — captures node scale values before/after - [ ] Implement `MoveLayerCommand` — captures layer transform before/after - [ ] Implement `ChangeNodeColorCommand` @@ -173,9 +172,12 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. - [ ] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) - [ ] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) - [ ] Migrate View Data panel (`views/data.R` → `src/ui/data.ts`) -- [ ] Migrate Help panel (`views/help.R` → `src/ui/help.ts`) +- [ ] Migrate FPS panel (`views/fps.R` → `src/ui/fps.ts`) +- [ ] Migrate Help panel (`views/help.R` → `src/ui/help.ts`) — 920 lines of mostly static HTML; port as an HTML fragment, not TS +- [ ] Migrate footer (`views/footer.R` → static footer in `index.html`) +- [ ] Port edge-panel toggles from `functions/edges.R` into `src/ui/edge.ts`, then delete it - [ ] Add Undo/Redo buttons wired to `CommandHistory` -- [ ] Delete `views/`, `ui.R`, `server.R`, `global.R`, `www/arena3dweb.css` +- [ ] Delete `views/`, `ui.R`, `server.R`, `www/arena3dweb.css` --- diff --git a/SPEC.md b/SPEC.md index cb3ef7f..5e5572d 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,6 +1,6 @@ # Arena3Dweb v3 — Restack Design Spec -**Date:** 2026-04-06 +**Date:** 2026-04-06 (revised 2026-07-02 — python-igraph over NetworkX, clustering folded into layout, stored external-API tokens, VR endpoint surfaced) **Status:** Approved for implementation planning --- @@ -29,7 +29,7 @@ Primary users are end users on the hosted version (arena3d.org). The deployment | 3D engine | Three.js (npm) | Manually patched `three.js` copy | | UI styling | Bootstrap 5 | Shiny/shinythemes (Darkly) | | Backend | FastAPI (Python) | R/Shiny | -| Graph algorithms | NetworkX + scipy + python-louvain | igraph (R) | +| Graph algorithms | python-igraph + pandas | igraph (R) | | API contract | OpenAPI (auto-generated by FastAPI) | None | | Container | Docker (slim Python base + nginx) | r-base Docker image | @@ -39,7 +39,11 @@ The existing frontend is already OOP (`Scene`, `Layer`, `Node`, `Edge` classes w ### Why FastAPI over Node.js -NetworkX mirrors igraph's API closely, making the algorithm port mechanical. Python is the lingua franca in bioinformatics — student contributors almost certainly know it. FastAPI is designed for rich REST APIs with automatic OpenAPI documentation. Node.js graph algorithm libraries are significantly less mature than Python's ecosystem. +python-igraph wraps the same C core as R's igraph, making the algorithm port a 1:1 rename with identical output. Python is the lingua franca in bioinformatics — student contributors almost certainly know it. FastAPI is designed for rich REST APIs with automatic OpenAPI documentation. Node.js graph algorithm libraries are significantly less mature than Python's ecosystem. + +### Why python-igraph over NetworkX + +The v2 UI exposes 11 layout algorithms (Fruchterman-Reingold, Reingold-Tilford, Circle, Grid, Random, DrL, Graphopt, Kamada-Kawai, Large Graph Layout, MDS, Sugiyama) and 4 clustering algorithms (Louvain, Walktrap, Fast Greedy, Label Propagation). NetworkX has no equivalent for DrL, Graphopt, LGL, Sugiyama, Reingold-Tilford, Walktrap, or Fast Greedy — each would need to be dropped, approximated, or hand-ported. python-igraph is the same C library R calls today, so every algorithm ports exactly with matching output, and one dependency replaces three (networkx, scipy, python-louvain). --- @@ -59,13 +63,13 @@ The app splits into two independently deployable units communicating over HTTP: ┌────────────────▼────────────────────────────┐ │ FastAPI (Python) │ │ - Network file parsing & validation │ -│ - Graph algorithms (NetworkX) │ +│ - Graph algorithms (python-igraph) │ │ - Session import/export │ │ - Public REST API (OpenAPI docs at /docs) │ └─────────────────────────────────────────────┘ ``` -**Stateless server.** The FastAPI backend holds zero per-user state between requests. The frontend (TypeScript state store) is the single source of truth. Every algorithm request carries its own complete graph payload; the server is a pure computation service. This means simultaneous users are naturally isolated — no session management, no memory leaks, and the backend is horizontally scalable behind a load balancer without additional infrastructure. +**Stateless server.** The FastAPI backend holds zero per-user state between requests. The frontend (TypeScript state store) is the single source of truth. Every algorithm request carries its own complete graph payload; the server is a pure computation service. This means simultaneous users are naturally isolated — no session management, no memory leaks, and the backend is horizontally scalable behind a load balancer without additional infrastructure. (One deliberate exception: the external-API and VR endpoints write short-lived files to `tmp/` with TTL cleanup, exactly as v2 does — see §5.) **Serving in production.** nginx serves the static Vite build and proxies `/api/*` to uvicorn. One Docker image, one container. `docker run -p 8080:8080 arena3dweb` works identically to today. @@ -111,7 +115,7 @@ class CommandHistory { } ``` -Commands to implement: `ApplyLayoutCommand`, `ApplyClusteringCommand`, `ApplyTopologyCommand`, `MoveLayerCommand`, `ChangeNodeColorCommand`, `ChangeNodeSizeCommand`, `ChangeEdgeColorCommand`, `ChangeThemeCommand`, `LoadNetworkCommand`. +Commands to implement: `ApplyLayoutCommand` (also carries cluster IDs/colors when the layout ran with clustering — one API response, one undo step), `ApplyTopologyCommand`, `MoveLayerCommand`, `ChangeNodeColorCommand`, `ChangeNodeSizeCommand`, `ChangeEdgeColorCommand`, `ChangeThemeCommand`, `LoadNetworkCommand`. File upload and session export are NOT commands — they are initialization/read-only events. @@ -149,30 +153,25 @@ UI panels read from the store. Commands update it. The event bus notifies the sc ### Backend -**Strategy Pattern** — plug-in layout, clustering, and topology algorithms. +**Registry of plain functions** — plug-in layout, clustering, and topology algorithms. ```python -# app/services/algorithms/base.py -class LayoutStrategy(ABC): - @abstractmethod - def compute(self, graph: nx.Graph, **kwargs) -> dict[str, list[float]]: ... - -# app/services/algorithms/registry.py -LAYOUTS: dict[str, LayoutStrategy] = { - "force": ForceDirectedLayout(), - "circle": CircularLayout(), - "grid": GridLayout(), - "random": RandomLayout(), +# app/services/layouts.py +LAYOUTS: dict[str, Callable[[ig.Graph], list[tuple[float, float]]]] = { + "Fruchterman-Reingold": lambda g: g.layout_fruchterman_reingold(weights="weight"), + "Kamada-Kawai": lambda g: g.layout_kamada_kawai(), + "Circle": lambda g: g.layout_circle(), + # ... one entry per v2 UI option } ``` -Adding a new algorithm = add one file + one line in the registry. The router never changes. Each strategy is independently unit-testable. +Adding a new algorithm = one dict entry. The router never changes. No abstract base classes — each strategy is a one-line igraph call, so a class hierarchy is pure ceremony; introduce one only if a strategy ever grows real state. **Service Layer** — thin routers, all business logic in services. ``` routers/layout.py → validates request, calls services/graph.py -services/graph.py → builds nx.Graph, calls algorithm strategy +services/graph.py → builds ig.Graph (channel filter, subgraph scope, simplify rules) services/parser.py → TSV/JSON parsing and validation ``` @@ -186,9 +185,9 @@ services/parser.py → TSV/JSON parsing and validation |---|---|---| | `GET` | `/api/config` | `handler_initializeGlobals` | | `POST` | `/api/network` | `handleUploadNetwork()` | -| `POST` | `/api/layout` | `functions/igraph/layout.R` | -| `POST` | `/api/cluster` | `functions/igraph/cluster.R` | +| `POST` | `/api/layout` | `functions/igraph/layout.R` + `cluster.R` — clustering is an optional layout parameter, not a standalone action (see flow below) | | `POST` | `/api/topology` | `functions/igraph/topology.R` | +| `POST` | `/api/vr` + `GET /api/vr/` | `functions/vr.R` — PLY + A-Frame HTML generation *(keep/drop decision pending, see PLAN Phase 7)* | | `POST` | `/api/session/import` | `importNetwork()` | | `POST` | `/api/session/export` | `convertSessionToJSON()` | | `POST` | `/api/external` | `resolveAPI()` | @@ -214,27 +213,33 @@ POST /api/network { file: } → history.execute(new LoadNetworkCommand(response)) ``` -**Layout request** +**Layout request** (mirrors the v2 "Layer Selection & Layouts" tab exactly) ``` -POST /api/layout { nodes, edges, algorithm, selected_layers, options } -← { positions: { "NodeA::LayerA": [x, y, z], ... } } -→ history.execute(new ApplyLayoutCommand(response.positions)) +POST /api/layout { nodes, edges, algorithm, + scope: "perLayer" | "allLayers" | "nodesPerLayers", + selected_layers, selected_nodes?, selected_channels?, + clustering?: { algorithm, local_layout } } +← { positions: { "NodeA::LayerA": [y, z], ... }, + clusters?: { "NodeA::LayerA": 0, ... } } +→ history.execute(new ApplyLayoutCommand(response)) ``` -**Clustering** -``` -POST /api/cluster { nodes, edges, algorithm, selected_layers } -← { clusters: { "NodeA::LayerA": 0, ... }, cluster_count: 4 } -→ history.execute(new ApplyClusteringCommand(response.clusters)) -``` +Notes carried over from v2 behaviour: +- Coordinates are **2D within the layer plane** (x is fixed by the layer), hence `[y, z]`. +- Circle/Grid/Random (`NO_EDGE_LAYOUTS`) run on a pseudo-network that links isolated nodes with tiny weights (`filterPseudoNetwork()`), so every node of the layer participates. +- When `clustering` is given, the server runs the v2 supernode strategy: cluster, lay out cluster super-nodes with the global algorithm (with repelling force), lay out members with `local_layout`, translate into place. Response then includes `clusters` (feeds the Clustering Data table and node-color priority). +- v2 fixes the RNG (`set.seed(123)`); the port must seed per request for reproducible layouts. **Topology metrics** ``` -POST /api/topology { nodes, edges, metric, selected_layers } -← { values: { "NodeA::LayerA": 0.82, ... }, min: 0.0, max: 1.0 } -→ history.execute(new ApplyTopologyCommand(response.values)) +POST /api/topology { nodes, edges, metric, scope, selected_layers, + selected_nodes?, selected_channels?, directed? } +← { scales: { "NodeA::LayerA": 1.4, ... }, raw: { "NodeA::LayerA": 12, ... } } +→ history.execute(new ApplyTopologyCommand(response.scales)) ``` +`scales` is mapped server-side into `[TARGET_NODE_SCALE_MIN, TARGET_NODE_SCALE_MAX]` as v2's `mapper()` does; `raw` feeds the View Data metric table. + **Session export** ``` POST /api/session/export { layers, nodes, edges, scene } @@ -258,21 +263,34 @@ GET /api/external/ ← full session JSON → same as session import flow ``` -The token is a base64-encoded compressed representation of the session JSON — no server-side storage required, keeping the stateless model intact. The URL is self-contained. +The token is a short random ID; the session JSON is stored server-side in `tmp/` with TTL cleanup — exactly the mechanism v2 already uses (`POST_REQUEST_PATH`). A base64-in-URL scheme was considered to stay fully stateless, but sessions can reach `MAX_EDGES = 10,000` edges, far past practical URL length limits (~2 KB); it would silently break for the networks the external API exists for. These ephemeral files (plus the VR output) are the sole stateful exception. + +### igraph (R) → python-igraph mapping -### igraph → NetworkX mapping +Same C core — every call is a rename with identical semantics. -| R/igraph | Python/NetworkX | +| R/igraph | python-igraph | |---|---| -| `graph_from_data_frame()` | `nx.from_pandas_edgelist()` | -| `layout_with_fr()` | `nx.spring_layout()` | -| `layout_with_kk()` | `nx.kamada_kawai_layout()` | -| `layout_in_circle()` | `nx.circular_layout()` | -| `cluster_louvain()` | `community.best_partition()` (python-louvain) | -| `cluster_walktrap()` | `nx.community.asyn_lpa_communities()` (LPA — closest available; exact walktrap port via `igraph` Python bindings if needed) | -| `betweenness()` | `nx.betweenness_centrality()` | -| `transitivity()` | `nx.clustering()` | -| `degree()` | `nx.degree_centrality()` | +| `graph_from_data_frame()` | `Graph.DataFrame()` / `Graph.TupleList()` | +| `simplify()` | `Graph.simplify()` | +| `layout_with_fr()` | `Graph.layout_fruchterman_reingold()` | +| `layout_as_tree()` (Reingold-Tilford) | `Graph.layout_reingold_tilford()` | +| `layout_in_circle()` | `Graph.layout_circle()` | +| `layout_on_grid()` | `Graph.layout_grid()` | +| `layout_randomly()` | `Graph.layout_random()` | +| `layout_with_drl()` | `Graph.layout_drl()` | +| `layout_with_graphopt()` | `Graph.layout_graphopt()` | +| `layout_with_kk()` | `Graph.layout_kamada_kawai()` | +| `layout_with_lgl()` | `Graph.layout_lgl()` | +| `layout_with_mds()` | `Graph.layout_mds()` | +| `layout_with_sugiyama()` | `Graph.layout_sugiyama()` | +| `cluster_louvain()` | `Graph.community_multilevel()` | +| `cluster_walktrap()` | `Graph.community_walktrap()` | +| `cluster_fast_greedy()` | `Graph.community_fastgreedy()` | +| `cluster_label_prop()` | `Graph.community_label_propagation()` | +| `degree()` | `Graph.degree()` | +| `betweenness()` | `Graph.betweenness()` | +| `transitivity(type = "weighted")` | `Graph.transitivity_local_undirected(weights=…)` | --- @@ -306,12 +324,10 @@ Arena3Dweb/ ← same GitHub repo, v3 branch routers/ # one file per endpoint group services/ parser.py # ports functions/input.R - graph.py # nx.Graph construction helpers - algorithms/ - base.py # abstract Strategy base classes - layouts/ # force.py, circle.py, grid.py, random.py - clusters/ # louvain.py, walktrap.py - topology/ # degree.py, betweenness.py, clustering_coeff.py + graph.py # ig.Graph construction: channel filter, subgraph scope, simplify rules + layouts.py # name → Graph.layout_* registry + pseudo-network for no-edge layouts + clustering.py # name → Graph.community_* registry + supernode clustered-layout strategy + topology.py # degree / betweenness / clustering coefficient + scale mapper tests/ fixtures/ # reuse www/data/*.json and TSV files directly test_network.py @@ -392,7 +408,7 @@ Example sequence: 2. Port + test POST /api/config → delete config/server_variables.R 3. Port + test POST /api/network → delete functions/input.R 4. Port + test POST /api/layout → delete functions/igraph/layout.R -5. Port + test POST /api/cluster → delete functions/igraph/cluster.R +5. Port + test clustered layout → delete functions/igraph/cluster.R 6. Port + test POST /api/topology → delete functions/igraph/topology.R 7. Port + test session import/export → delete functions/init.R, functions/general.R 8. Migrate Three.js classes → TypeScript → delete www/js/classes/ From 1d20b9407728c203b732bab5ce1c4d5b51a0dcb4 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 19:35:26 +0100 Subject: [PATCH 004/166] backend: switch to uv + python-igraph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace networkx/scipy/python-louvain with python-igraph (same C core as R igraph — all v2 algorithms port 1:1, see SPEC). Package management via uv: deps in pyproject.toml + uv.lock, requirements.txt deleted. Remove stale algorithms/ scaffold dirs (SPEC now uses flat service modules). Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 13 +- PLAN.md | 2 +- SPEC.md | 4 +- backend/app/services/algorithms/__init__.py | 0 .../services/algorithms/clusters/__init__.py | 0 .../services/algorithms/layouts/__init__.py | 0 .../services/algorithms/topology/__init__.py | 0 backend/pyproject.toml | 7 +- backend/requirements.txt | 12 - backend/uv.lock | 1219 +++++++++++++++++ 10 files changed, 1231 insertions(+), 26 deletions(-) delete mode 100644 backend/app/services/algorithms/__init__.py delete mode 100644 backend/app/services/algorithms/clusters/__init__.py delete mode 100644 backend/app/services/algorithms/layouts/__init__.py delete mode 100644 backend/app/services/algorithms/topology/__init__.py delete mode 100644 backend/requirements.txt create mode 100644 backend/uv.lock diff --git a/CLAUDE.md b/CLAUDE.md index 357a392..0873dca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,13 +16,11 @@ If you are on the `v3` branch, the presence of old R/Shiny files (`server.R`, `f ### v3 (FastAPI + Vite — active development, `v3` branch) -**Backend:** +**Backend** (package management via `uv` — no manual venv/pip): ```bash cd backend -python3 -m venv .venv -source .venv/bin/activate -pip install -r requirements.txt -uvicorn app.main:app --reload # http://localhost:8000 +uv sync # installs deps + dev group into .venv +uv run uvicorn app.main:app --reload # http://localhost:8000 ``` **Frontend:** @@ -39,7 +37,7 @@ docker-compose up **Backend tests:** ```bash -cd backend && pytest +cd backend && uv run pytest ``` **Frontend tests:** @@ -50,7 +48,8 @@ cd frontend && npm run test:e2e # Playwright E2E **Lint / format:** ```bash -cd backend && ruff check . && ruff format . +cd backend && uv run ruff check . && uv run ruff format . +cd backend && uv run mypy app cd frontend && npm run lint && npm run format ``` diff --git a/PLAN.md b/PLAN.md index 4ea2635..2cc0d46 100644 --- a/PLAN.md +++ b/PLAN.md @@ -51,7 +51,7 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. ## Phase 4 — Backend: Layout Algorithms -- [ ] Swap `networkx` + `scipy` + `python-louvain` for `python-igraph` in `requirements.txt` (SPEC §2 — same C core as R igraph, all 11 UI layouts port exactly) +- [x] Swap `networkx` + `scipy` + `python-louvain` for `python-igraph`; backend now managed by `uv` (`pyproject.toml` + `uv.lock`, `requirements.txt` deleted) - [ ] Implement `services/graph.py` — `ig.Graph` construction (port `functions/igraph/general.R`: channel filter, perLayer/allLayers/nodesPerLayers subgraph scopes, `simplify()` multi-edge/loop rules) - [ ] Implement `services/layouts.py` — registry dict mapping all 11 UI layout names to `Graph.layout_*` calls (port `getLayoutFunction()`) - [ ] Port pseudo-network for no-edge layouts (`NO_EDGE_LAYOUTS` = Circle, Grid, Random) — `filterPseudoNetwork()` chains isolated nodes with tiny weights so whole layer participates diff --git a/SPEC.md b/SPEC.md index 5e5572d..a80605f 100644 --- a/SPEC.md +++ b/SPEC.md @@ -336,8 +336,8 @@ Arena3Dweb/ ← same GitHub repo, v3 branch test_topology.py test_session.py test_external.py - pyproject.toml # Ruff + mypy config - requirements.txt + pyproject.toml # deps (uv) + Ruff + mypy config + uv.lock docs/ superpowers/specs/ nginx/ diff --git a/backend/app/services/algorithms/__init__.py b/backend/app/services/algorithms/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app/services/algorithms/clusters/__init__.py b/backend/app/services/algorithms/clusters/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app/services/algorithms/layouts/__init__.py b/backend/app/services/algorithms/layouts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/app/services/algorithms/topology/__init__.py b/backend/app/services/algorithms/topology/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 8f4b2e0..58ccbc0 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -5,14 +5,13 @@ requires-python = ">=3.11" dependencies = [ "fastapi>=0.115.0", "uvicorn[standard]>=0.30.0", - "networkx>=3.3", - "scipy>=1.14.0", - "python-louvain>=0.16", + "igraph>=0.11.0", "pandas>=2.2.0", "pydantic>=2.8.0", + "python-multipart>=0.0.9", ] -[project.optional-dependencies] +[dependency-groups] dev = [ "httpx>=0.27.0", "pytest>=8.3.0", diff --git a/backend/requirements.txt b/backend/requirements.txt deleted file mode 100644 index e9a9ded..0000000 --- a/backend/requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -fastapi>=0.115.0 -uvicorn[standard]>=0.30.0 -networkx>=3.3 -scipy>=1.14.0 -python-louvain>=0.16 -pandas>=2.2.0 -pydantic>=2.8.0 -httpx>=0.27.0 -pytest>=8.3.0 -pytest-asyncio>=0.24.0 -mypy>=1.11.0 -ruff>=0.6.0 diff --git a/backend/uv.lock b/backend/uv.lock new file mode 100644 index 0000000..c0bdbff --- /dev/null +++ b/backend/uv.lock @@ -0,0 +1,1219 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" }, +] + +[[package]] +name = "arena3dweb" +version = "3.0.0" +source = { virtual = "." } +dependencies = [ + { name = "fastapi" }, + { name = "igraph" }, + { name = "pandas" }, + { name = "pydantic" }, + { name = "python-multipart" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.dev-dependencies] +dev = [ + { name = "httpx" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "fastapi", specifier = ">=0.115.0" }, + { name = "igraph", specifier = ">=0.11.0" }, + { name = "pandas", specifier = ">=2.2.0" }, + { name = "pydantic", specifier = ">=2.8.0" }, + { name = "python-multipart", specifier = ">=0.0.9" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.30.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "httpx", specifier = ">=0.27.0" }, + { name = "mypy", specifier = ">=1.11.0" }, + { name = "pytest", specifier = ">=8.3.0" }, + { name = "pytest-asyncio", specifier = ">=0.24.0" }, + { name = "ruff", specifier = ">=0.6.0" }, +] + +[[package]] +name = "ast-serialize" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/ad/0d70a3a2d6e01968d985415259e8ec7ad3f777903f9b1c1f3c8c44642c60/ast_serialize-0.6.0.tar.gz", hash = "sha256:aadd3ffcf4858c9726bf3515f7b199c7eadbe504f96028e4a87172c0da65a8fe", size = 61489, upload-time = "2026-06-30T20:02:55.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/12/3e5f575f156555547c250a8b0d1347517a3a20fc7f4492e9703a69d4f45e/ast_serialize-0.6.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:a7520b672827885bafeae7501f684d14d47d17e5f45256f9df547686cca52264", size = 1177640, upload-time = "2026-06-30T20:02:06.708Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a4/921a9e27951627983b0f368859ea00f8330a551dc0bf4c2fdcb11855a98b/ast_serialize-0.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a14191beec7e0c078d2fc1f6edc0aee88bcd4db9f18e1bc9f8052b559c22dddc", size = 1168111, upload-time = "2026-06-30T20:02:08.366Z" }, + { url = "https://files.pythonhosted.org/packages/00/69/950cf404de7b8782cf95e5c1237e25e2aa46177b287f39f9eeddf481fd6f/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32ef62ec34cf6be20ad77d4799556638fbdf187f3ae10698dfb20ef9f2c89516", size = 1227656, upload-time = "2026-06-30T20:02:09.843Z" }, + { url = "https://files.pythonhosted.org/packages/4c/a8/46f8f6a6479d9d2273980957bb091a506c55f5b95d3c029ee58518a78407/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13b7769970a39983b0adf2f38917b1cd3b8946f76df045756c3d741bc689f089", size = 1227706, upload-time = "2026-06-30T20:02:11.367Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b9/9ac415bda0a40e49eab8fea3b2741c19c98bb84d57d62c4cfc6230eb67be/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f7a408601bb3edaefb3bc67a4c01f5235e3253653b6a5729a2ee2382b35341c", size = 1431705, upload-time = "2026-06-30T20:02:12.737Z" }, + { url = "https://files.pythonhosted.org/packages/e5/06/8807115d441444879f7561b5eede5ac18fc80392f11826d61ccf31f503b1/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8670bfa51208a2c0c8d138928e40e998fab158f9200d53bb80c088b5b8eda7b8", size = 1249533, upload-time = "2026-06-30T20:02:14.571Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c0/c2ba82ef9618650357d9421a1fdb27ffec862a7f57e8e2de82a3ccd11e12/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4826809eb8597a8cd59fd924b6d7c285b8969a1e0007e2cb652cab62376270f", size = 1252619, upload-time = "2026-06-30T20:02:16.219Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a7/fa31d52dd4102cede29fb9634e98d214129b2783b4f95528c6dc6a8f6587/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:577a6c189068686869f5f1ddc38363f3ae1808a4753b577266f9202071a7bb66", size = 1242983, upload-time = "2026-06-30T20:02:17.813Z" }, + { url = "https://files.pythonhosted.org/packages/b1/20/ddf742b5ad3c4bafd3466f2265037cfd99bc1b9a5ee46a5d58c90d523242/ast_serialize-0.6.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085de7f62dc9cc247eb01e965a362707d1d90b1d89a82c5bf78301a60a3c417b", size = 1296148, upload-time = "2026-06-30T20:02:19.146Z" }, + { url = "https://files.pythonhosted.org/packages/24/cb/9f6f217cce8b3b632c5568b478d195a35e79dce4dbe309438cb89ba6ea4f/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9f8a8b78b13173de6a9ec22111d9be674874cd5bdccda04f14ae5ebc2bef403a", size = 1403826, upload-time = "2026-06-30T20:02:20.696Z" }, + { url = "https://files.pythonhosted.org/packages/2d/f8/9d16d4f0107a183924425cc0e7618d8bf76f96b45afa9ff19f924ed1ad57/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f2ff3baffc3a29c1f15bc9098aa0c09763410262d5e6cef42116f7356c184554", size = 1502943, upload-time = "2026-06-30T20:02:22.034Z" }, + { url = "https://files.pythonhosted.org/packages/80/dd/bbc1c38756350dddf7e24acae1c9482ef42051c267417e019aecc1ed4075/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0067b25fce104eaae5b88383de9ab803faeb671831e14ca698b771b356e2600f", size = 1497632, upload-time = "2026-06-30T20:02:23.517Z" }, + { url = "https://files.pythonhosted.org/packages/42/7e/9daffefcf5b97e6bb4c3e0b3c024c1aee9722f23d3cf7cd2ff80d6fb4a40/ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c617417f9cbb0cb144f6283c3cbe0d2e0f01beaf9f608f662b21191058a626ec", size = 1448858, upload-time = "2026-06-30T20:02:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1f/f9baaab81a677ea0af7d2458cac2f94ebcc85958f8a3c15ba9d9e5dab653/ast_serialize-0.6.0-cp314-cp314t-win32.whl", hash = "sha256:5337cb256dcea3df9288205213d1601581536526b8f4da44b6974f1180f3252a", size = 1052600, upload-time = "2026-06-30T20:02:26.263Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1f/41b535866519512d8cf6669cb2cff7823b7672bb6279c0333b4ff89d7d9f/ast_serialize-0.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2d947e45cafc4b09bd7528917fa84c517654a43de173c79785574b7b3068ac24", size = 1095570, upload-time = "2026-06-30T20:02:27.639Z" }, + { url = "https://files.pythonhosted.org/packages/50/64/e472fe3e3a2d33d874b987e8518aedf24562919e3b6161a4fa1797e89c0f/ast_serialize-0.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6e15ec740436e1a0d62de848641abe5f3a2f89a7f94907d534795ac91bbacf14", size = 1067267, upload-time = "2026-06-30T20:02:28.949Z" }, + { url = "https://files.pythonhosted.org/packages/52/19/ac8348ae8711c9b5ae834634f635780cab62a0f5e6f988882e048b89c2ae/ast_serialize-0.6.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:093cb8bb91b720d8523580498d031791bb1bbaa048599c3d21085d380e11a596", size = 1185367, upload-time = "2026-06-30T20:02:30.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f6/ec7ec652c51db77c2f61d8573338e13e4704303265ccc658cb4031d9f354/ast_serialize-0.6.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e61580a69faf47e3689795367ed211f2a10fd741478cc0f36a0f128793360aad", size = 1178657, upload-time = "2026-06-30T20:02:31.964Z" }, + { url = "https://files.pythonhosted.org/packages/6f/02/613a7534a41d0122f37d1e0c64aa8ac78bfb831f8c92f6db057a311abb3c/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305802f2ce2a7c4e87835078ea85c58b586ddda8095b92fe2ead9364ae19c80a", size = 1238620, upload-time = "2026-06-30T20:02:33.664Z" }, + { url = "https://files.pythonhosted.org/packages/4d/21/087957bba486242afc52f49b2d9e21c9dad00289356cf9efe67084015a9d/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7b8b8f0c42f752ea00b2b7d7c090b3f80d9c1c5c75cadf16423790a0cc74081", size = 1236075, upload-time = "2026-06-30T20:02:34.936Z" }, + { url = "https://files.pythonhosted.org/packages/82/04/78128bbb170071c2c72a210a181f1c00e11cc1cec60a8beef747b07f9201/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd5b91b9e6f2356ace3a556963b0cd783b395fbbb0bb17b4defc283415466e77", size = 1441348, upload-time = "2026-06-30T20:02:36.245Z" }, + { url = "https://files.pythonhosted.org/packages/64/64/62fb99d6faf199b4c3e5b08a07136e9a0d7664bb249c6de3670e5b63e9b6/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d6ef91590258ada18909b9caea344dac4de2013906b035473cd674a43f4b790", size = 1258580, upload-time = "2026-06-30T20:02:37.53Z" }, + { url = "https://files.pythonhosted.org/packages/ca/87/b4d6c38e0ccd5e85dc54cecdf933a152c60b28fe5d993a6d8a72fa6d5896/ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcbed41e9386059fc0261d602445ede0976c2ecec2939688bcbcb9ed0b6f28b7", size = 1261693, upload-time = "2026-06-30T20:02:39.123Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4b/3676ca2191f39bafb75f93f99b2f429ec464586158fece2165f3572805dc/ast_serialize-0.6.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:cdc4e6f930b9090c2f92c9036ad12ffb8e6e44d4a5ba06f1458a05d60f203f7b", size = 1252517, upload-time = "2026-06-30T20:02:40.511Z" }, + { url = "https://files.pythonhosted.org/packages/f3/58/494ef8c4b4acb2f4a265ac934caf45f792a08fe27d6b853de35ad991941a/ast_serialize-0.6.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:897ac47b5637be41c0c07061c8a912fafa967ef1dc73fa115e4bfa70882a093b", size = 1304843, upload-time = "2026-06-30T20:02:41.961Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f2/13736d920ab3d49bbee80ef1a277dd7b7aaf3b3545efd9d2a8114fe05525/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c4af9a1386166e40ed01464991806f89038a2d89782576c7774876fa77034e32", size = 1413698, upload-time = "2026-06-30T20:02:44.179Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5a/e046f3899e2acba4677d7427b76431443a1aa1a0e583dfb05b55b69d55cf/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:c901adbd750029b9ac4ad3d6aa56853e0ad4875119fbf52b7b8298afc223828b", size = 1512209, upload-time = "2026-06-30T20:02:45.584Z" }, + { url = "https://files.pythonhosted.org/packages/cc/c7/e42aaca7bb2d22a7c06d5a8c7930086c5a334e93d716e6fa5e6647a4515f/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae22a366b752ab4496191525b78b097b5b72d531752e3c1dd7e383a8f2c8a1a", size = 1508464, upload-time = "2026-06-30T20:02:46.942Z" }, + { url = "https://files.pythonhosted.org/packages/95/93/5524a3dc6c3f593de3228ed9cbef73afa047625b7000ec21b7f58e6eb4d4/ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4ed29121da8b3fdc291002801a1de0f76248fa07dce89157a5f277842cf6126e", size = 1457164, upload-time = "2026-06-30T20:02:48.294Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c0/36a6ffb4d653cf621427b4c4928671f53ad800c453474de2b82564a44ad9/ast_serialize-0.6.0-cp39-abi3-pyemscripten_2026_0_wasm32.whl", hash = "sha256:b1dac4e09d341c1300ba69cdcbe62867b32a8c75d90db9bf4d083bec3b039f0b", size = 863014, upload-time = "2026-06-30T20:02:49.742Z" }, + { url = "https://files.pythonhosted.org/packages/09/c7/7d5ad8b49e1278e1c2a1e0274bd7850560b3f09313aa00c13bc8d5544792/ast_serialize-0.6.0-cp39-abi3-win32.whl", hash = "sha256:82c312a7844d2fdeb4d5c48bd3d215bf940dafd4704e1a9bcf252a99010a99b1", size = 1063165, upload-time = "2026-06-30T20:02:50.98Z" }, + { url = "https://files.pythonhosted.org/packages/47/ae/6710c14ecb276031cf10249f6adf5a59e2d3fdb3b5183bd59f70524067ee/ast_serialize-0.6.0-cp39-abi3-win_amd64.whl", hash = "sha256:113b58346f9ceb664352032770caca817d4a3c86f611c6088e6ef65ddaa70f0e", size = 1101444, upload-time = "2026-06-30T20:02:52.554Z" }, + { url = "https://files.pythonhosted.org/packages/66/40/c53deb2cd0c9b0fb636d24d9f40924cf2e65028e6b20b10cd5c1eeb2c730/ast_serialize-0.6.0-cp39-abi3-win_arm64.whl", hash = "sha256:ccd132fe8db56f61fe743b1f644d01b8d65b83248a8da506f3132bda86d6ed5e", size = 1072965, upload-time = "2026-06-30T20:02:54.097Z" }, +] + +[[package]] +name = "certifi" +version = "2026.6.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, +] + +[[package]] +name = "click" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "fastapi" +version = "0.139.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d3/af/a5f50ccfa659ec1802cb4ca842c23f06d906a8cc9aef6016a2caeea3d4ed/fastapi-0.139.0.tar.gz", hash = "sha256:99ab7b2d92223c76d6cf10757ab3f89d45b38267fc20b2a136cf02f6beac3145", size = 423016, upload-time = "2026-07-01T16:35:33.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/7c/8e3c6ad324ea5cb36604fc3f968554887891c316d9dfde57761611d907ad/fastapi-0.139.0-py3-none-any.whl", hash = "sha256:cf15e1e9e667ddb0ad63811e60bd11390d1aac838ca4a7a23f421807b2308189", size = 130339, upload-time = "2026-07-01T16:35:32.19Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httptools" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/e5/d471fcb0e14523fe1c3f4ba58ca52480e7bd70ad7109a3846bc75892f7fb/httptools-0.8.0.tar.gz", hash = "sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999", size = 271342, upload-time = "2026-05-25T22:17:48.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/d2/c3eedaef57de65c3cc5f8dc244cf12d09c84ad258a479055aad6db23206c/httptools-0.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed377e64805bdba4943c82717333f8f8603a13b09aff9cead2717c6c817fb168", size = 208428, upload-time = "2026-05-25T22:16:59.717Z" }, + { url = "https://files.pythonhosted.org/packages/f1/94/dfe435d90d0ef61ec0f2cc3d480eef78c59727c6c2ce039f433882f6131a/httptools-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9518c406d7b310f05adb1a37f80acabac40504a575d7c0da6d3e365c695ac20d", size = 113366, upload-time = "2026-05-25T22:17:00.795Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d4/13025f1a56e615dcb331e0bbe2d9a1143212b58c263385fc5d2e558f5bac/httptools-0.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:57278e6fa0424c42a8a3e454828ab4f0aff27b40cddf9679579b98c6dce6a376", size = 464676, upload-time = "2026-05-25T22:17:02.014Z" }, + { url = "https://files.pythonhosted.org/packages/bf/95/4c1c26c0b985f8a3331682d802598f14e32dc41bf7509266eb2c04ad4801/httptools-0.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bbb8caadb2b742d293169d2b458b5c001ef70e3158704aa3d3ef9597624c5d1d", size = 464235, upload-time = "2026-05-25T22:17:03.109Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/6735be2b0ca527718c431cdb8e5f70c3862c0844a687df0f572c51e11497/httptools-0.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:52dd695b865fe96d9d2b16b64a895f3f57bf3cb064e8383cd3b5713a069e8085", size = 449809, upload-time = "2026-05-25T22:17:04.443Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f9/5811c74f37a758c8a4aa3dc430375119d335947e883efc4664d8f3559a41/httptools-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20b4aac66ff65f7db06a375808b78f42a94970aa22e826b3cb2b43eb09174124", size = 452174, upload-time = "2026-05-25T22:17:05.476Z" }, + { url = "https://files.pythonhosted.org/packages/cc/94/97b75870dea07b71e3ec535cebe525b08d723152e4c7d13fa887e51f4de2/httptools-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1b4c8e7a489a0d750d91894e9a8cdc295838f1924c0ca903ae993456fddec07", size = 90991, upload-time = "2026-05-25T22:17:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/14/88/1d21a36da8f5cb0fa49eafd4b169eba5608d57e75bbcf61845cbc6243216/httptools-0.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:880490234c10f70a9830743097e8958d6e4b9f5a0ffc24515023afeef984054d", size = 208247, upload-time = "2026-05-25T22:17:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/cc4feea2945cb3051038f090c9b36bd5b8a9d7f5a894a506a8983e33fd1c/httptools-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5931891fb7b441b8a3853cf1b85c82c903defce084dd5f6771ca46e31bf862c5", size = 113064, upload-time = "2026-05-25T22:17:09.136Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a6/febbb8b8db0f58b38e44ad6cb946e6a255ae49b55f2e8543408fb7501ccd/httptools-0.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b15fc622b0f869d19207c4089a501d9bcc63ca5e071ffdd2f03f922df882dcb2", size = 523851, upload-time = "2026-05-25T22:17:10.106Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e4/f90a0df0b83beff265b7e3b65f2a4cefd95792d4be0ac3e16049f2acd3c2/httptools-0.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:425f83884fd6343828d8c565f046cb72b6d19063f6924093e11bcd8e1548cd09", size = 518842, upload-time = "2026-05-25T22:17:11.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2d/0c9ac76dd2c893841fbf6498d6acec4f2442e1b7067f6e3e316a80e494e8/httptools-0.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7c3c97f4311c7be57e2986629df89d49cb434dbff78eafcd48c2bff986b15a", size = 501238, upload-time = "2026-05-25T22:17:12.728Z" }, + { url = "https://files.pythonhosted.org/packages/ca/42/906adc91ae3a5fa9c59c0a2f21c139725bd7e5b41ae6acd485cd14123ebf/httptools-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a1afd7c9fbff0d9f5d489c4ce2768bd09c84a46ddefc7161e6aa82ae35c85745", size = 509567, upload-time = "2026-05-25T22:17:13.842Z" }, + { url = "https://files.pythonhosted.org/packages/05/0b/4240efeb672751ee5b9b380cb0e3fdc050bc05f68adc7a8aefc4fcd9a69a/httptools-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd96f29b4bab1d42fa6e3d008711c75e0f79e94e06827330160e3a304227f150", size = 90918, upload-time = "2026-05-25T22:17:15.155Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e5/8cfcabc5546e8022f168be28bcdaa128a240a0befdd03b59d558b4f18bd6/httptools-0.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:614ceea8ea606848bece2338ac03b3ce5324bcb4be8dc7d377ed708012fa4db8", size = 205148, upload-time = "2026-05-25T22:17:16.333Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0e/0fb14848c19a686c8062ff9067c1a48793e3224b47bc5b201535b6036fce/httptools-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d689918c15a013c65ef52d9fd495d766893ab831a2c8d89f2ac5940a5df847c", size = 111368, upload-time = "2026-05-25T22:17:17.586Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/46f1cecf06b9bbde8e4b8c88034ac7908989e5ff7a3a388ef38392949c1f/httptools-0.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb3028cca2fc0a6d720e52ef61d8ebb62fcbfeb1de56874546d858d3f25a26b7", size = 486447, upload-time = "2026-05-25T22:17:18.564Z" }, + { url = "https://files.pythonhosted.org/packages/77/00/258bfc0837221f81d9725c45f9b948a6a6b2994a147a4fb66e85100c668f/httptools-0.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88bdd940f2b5d487b4d032c6afa5489a7dc4694410d43de3c38c4fb3af0dc45d", size = 482448, upload-time = "2026-05-25T22:17:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/04/ab/d1cef3b5523f4d272a70f42a776c3169a2dddfe3a54de4b2ce4a36341528/httptools-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a43c9dd399758ccc0531acb0a3c4a6c299ee893ee9400e9c893b7bdcfae0681", size = 464460, upload-time = "2026-05-25T22:17:20.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/5d1d072442277bb2b3434e0e60690b8e8c23840ef7de8b6ea54040a536d3/httptools-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683", size = 471312, upload-time = "2026-05-25T22:17:22.085Z" }, + { url = "https://files.pythonhosted.org/packages/0d/66/b96623b27e51a68199ef4efdda0613cced9233fe3062ac74e50749c5ad37/httptools-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:7685df791fad561384bfb139e77fde27a1ffd93134e016f95a0db424ffbf77b1", size = 90117, upload-time = "2026-05-25T22:17:23.074Z" }, + { url = "https://files.pythonhosted.org/packages/1a/12/fa3fbf5f9517b273edea2dc982aa82a8c634091e67c590792b729017bc6f/httptools-0.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:de242a49b5d18e0a8776e654e9f6bf6d89f3875a5c35b425a0e7ce940feb3fd6", size = 206183, upload-time = "2026-05-25T22:17:24.004Z" }, + { url = "https://files.pythonhosted.org/packages/30/fc/5e7c4cb443370f2090a3aba0453a07384d29ff66b7435bb90e77e1037599/httptools-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:159e9ab5f701ccd42e555a12f1ad8ff69702910fc1c996cf2bb66e5fcb7a231b", size = 112079, upload-time = "2026-05-25T22:17:25.216Z" }, + { url = "https://files.pythonhosted.org/packages/ba/53/771bd891eb0f236f32145d6a1775777ec85745f3cc983a1f23d1a3b8ddfe/httptools-0.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c4a9f1707e4823d54dfec6c33fa3697d302aed536ed352a7ebb5a061ddb869d0", size = 481596, upload-time = "2026-05-25T22:17:26.186Z" }, + { url = "https://files.pythonhosted.org/packages/62/42/94e15bc68ce3d423243c45d7f1b0c7561f13844f97dc52ae23182fb65628/httptools-0.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d76ad7b951387e3632c8716a9bb03ac5b45c5f16119aa409db0459520887944e", size = 480865, upload-time = "2026-05-25T22:17:27.542Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7c/fe2980fc03723272e30f135b62360b075f513dfe7cc73aef36c7f04012bd/httptools-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a3b7387147361c3fd47a0bde763c5c91b5b4cd4dc9989b8ece84ff436c99843b", size = 463189, upload-time = "2026-05-25T22:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/47fc5fff68acd1bfa20b4734059c9a06cadb88119dcd5258b5b0d21d91c8/httptools-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f256d6ce930c52ca1cb2a960b7da03548c454e7d28b06059ad41bfe789036ce0", size = 466610, upload-time = "2026-05-25T22:17:29.816Z" }, + { url = "https://files.pythonhosted.org/packages/60/bd/07b13c93ffd9bec9546e0d43f8e19378dd696dbd278511406bc07371ef1f/httptools-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:19d1ee275bb59ba2643ba9a3a1e51cc0c788caf2b8df506368e03f56fdd08527", size = 92705, upload-time = "2026-05-25T22:17:31.133Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c4/121648f68ce066d7bd762d6b6d97e620847642d38d54f3d90ff11d947629/httptools-0.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:de1ed58a974e75d56560acc7e7fed01a454994429456f65209789992e41f2568", size = 215023, upload-time = "2026-05-25T22:17:32.401Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b0/312a062ae741ae3e8baa8c8bf20be81b2e67337b259ab4349bebc7b6142e/httptools-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e93c227b595c6926c1acee96891dd9da4be338cfbe82e5cd3bb9d8dd7dc4ac0b", size = 117405, upload-time = "2026-05-25T22:17:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/fc/37/fccd705f795386bb05bf413012fecff2a33e5aa8c2f069096de3e9fd8702/httptools-0.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2a021c3a8e65cc125390d72f59b968afca3bdcaff25bd67965e0a055a14946ca", size = 558497, upload-time = "2026-05-25T22:17:34.732Z" }, + { url = "https://files.pythonhosted.org/packages/bd/39/f172e8003576de35f5ba77ff417cf0e34429d35dc014deef15afa337a72c/httptools-0.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48774d39cbb70e2b1f71f88852a3087ae1d3a1eb80482bb48c13067ab080c14f", size = 571585, upload-time = "2026-05-25T22:17:35.813Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b9/f5564760af99f3dbbf3f9104dc00e5da27e96cf433c6bdcf77617f70bf3f/httptools-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:88eead8ec8680a9f146c655bc88445a325bd7921cfd8194c7337e9467282427d", size = 543297, upload-time = "2026-05-25T22:17:37.08Z" }, + { url = "https://files.pythonhosted.org/packages/99/67/8d9f2c313618e161b82f3873188e7196126da1d6e29688df40eb3997c77a/httptools-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c032fa028f46871ec7e1fc59fc15e8023eab3e6bbe6ece786a1611719a5d081", size = 539535, upload-time = "2026-05-25T22:17:38.032Z" }, + { url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "igraph" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "texttable" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/be/56bef1919005b4caf1f71522b300d359f7faeb7ae93a3b0baa9b4f146a87/igraph-1.0.0.tar.gz", hash = "sha256:2414d0be2e4d77ee5357807d100974b40f6082bb1bb71988ec46cfb6728651ee", size = 5077105, upload-time = "2025-10-23T12:22:50.127Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/03/3278ad0ceb3ea0e84d8ae3a85bdded4d0e57853aeb802a200feb43847b93/igraph-1.0.0-cp39-abi3-macosx_10_15_x86_64.whl", hash = "sha256:c2cbc415e02523e5a241eecee82319080bf928a70b1ba299f3b3e25bf029b6d4", size = 2257415, upload-time = "2025-10-23T12:22:27.246Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bc/6281ec7f9baaf71ee57c3b1748da2d3148d15d253e1a03006f204aa68ca5/igraph-1.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a27753cd80680a8f676c2d5a467aaa4a95e510b30748398ec4e4aeb982130e8", size = 2048555, upload-time = "2025-10-23T12:22:29.49Z" }, + { url = "https://files.pythonhosted.org/packages/2a/38/3cd6428a4ed4c09a56df05998438e7774fd1d799ee4fb8fc481674f5f7fc/igraph-1.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a55dc3a2a4e3fc3eba42479910c1511bfc3ecb33cdf5f0406891fd85f14b5aee", size = 5314141, upload-time = "2025-10-23T12:22:31.023Z" }, + { url = "https://files.pythonhosted.org/packages/7d/da/dd2867c25adbb41563720f14b5fc895c98bf88be682a3faff4f7b3118d2a/igraph-1.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2d04c2c76f686fb1f554ee35dfd3085f5e73b7965ba6b4cf06d53e66b1955522", size = 5683134, upload-time = "2025-10-23T12:22:32.423Z" }, + { url = "https://files.pythonhosted.org/packages/e5/40/243c118d34ab80382d7009c4dcb99b887384c3d2ce84d29eeac19e2a007a/igraph-1.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2b52dc1757fff0fed29a9f7a276d971a11db4211569ed78b9eab36288dfcc9d", size = 6211583, upload-time = "2025-10-23T12:22:34.238Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b7/88f433819c54b496cb0315fce28e658970cb20ff5dbd52a5a605ce2888de/igraph-1.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:05c79a2a8fca695b2f217a6fa7f2549f896f757d4db41be32a055400cb19cc30", size = 6594509, upload-time = "2025-10-23T12:22:35.831Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5d/8f7f6f619d374e959aa3664ebc4b24c10abc90c2e8efbed97f2623fadaf5/igraph-1.0.0-cp39-abi3-win32.whl", hash = "sha256:c2bce3cd472fec3dd9c4d8a3ea5b6b9be65fb30edf760beb4850760dd4f2d479", size = 2725406, upload-time = "2025-10-23T12:22:37.588Z" }, + { url = "https://files.pythonhosted.org/packages/af/77/a85b3745cf40a0572bae2de8cd9c2a2a8af78e5cf3e880fc0a249114e609/igraph-1.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:faeff8ede0cf15eb4ded44b0fcea6e1886740146e60504c24ad2da14e0939563", size = 3221663, upload-time = "2025-10-23T12:22:39.404Z" }, + { url = "https://files.pythonhosted.org/packages/ef/7e/5df541c37bdf6493035e89c22bd53f30d99b291bcda6c78e9a8afeecec2b/igraph-1.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:b607cafc24b10a615e713ee96e58208ef27e0764af80140c7cc45d4724a3f2df", size = 2785701, upload-time = "2025-10-23T12:22:41.03Z" }, + { url = "https://files.pythonhosted.org/packages/b9/73/bf1d4dbbc9123435b3ca14bb608b243a50a4f158ecea564bf196715248d9/igraph-1.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3189c1a8e8a8f58009f3f729040eb3701254d074ed37245691d529869ec940c5", size = 2246636, upload-time = "2025-10-23T12:22:42.314Z" }, + { url = "https://files.pythonhosted.org/packages/59/ac/28482f2af45cc0a0ca88a69d17a6ea694f58bdbd22cc876e7273a0379282/igraph-1.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ebe9502689b946301584b3cfacdbc70c58c4d664d804e39b6daa31be5c20bf46", size = 2036101, upload-time = "2025-10-23T12:22:43.957Z" }, + { url = "https://files.pythonhosted.org/packages/56/80/806a093df1d1ddc3b30d0418b1ee56388ae7018f8ae288677ee2b3a1abaf/igraph-1.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f117683108c54330d6dc67a708e3724c13c9989885122a29781296872989a222", size = 3053403, upload-time = "2025-10-23T12:22:45.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/bf/cf7aeff230a4368c0b8bc6b02f3ea27db41db33714b51e1e8a7c1458f31b/igraph-1.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:077dbff0edb8b4ce0f9fefdf325200346d9d5db02de31872b41743de08e67a16", size = 3262472, upload-time = "2025-10-23T12:22:47.248Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ca/dbc06072d5eea402a6dc81f387afb1b7e0c415f1d8a75232943fc4d1bfdb/igraph-1.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fe7c693b2a84a4e03ca31e65aa05a2ecd8728137fa9909ccbf6453b4200b856d", size = 3218861, upload-time = "2025-10-23T12:22:48.46Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "librt" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/e0/dbd0f2a68a1c1a1991eb7921ff6014465d56608cdc9a9fb468a616210a37/librt-0.12.0.tar.gz", hash = "sha256:cb26faedbd09c6130e9c1b64d8000efec5076ffd18d606c6cd1cf02730e6d8b0", size = 203841, upload-time = "2026-06-30T16:14:29.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/ab/628490f42d1eba82f3c7e5821aa62013e6df7f525b7a9e92c048f8d1cc1c/librt-0.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f13c1e8563102c2b17581cf37fcb2c6dae7ad485ccea93ae46258998c25f9a1", size = 143821, upload-time = "2026-06-30T16:12:23.248Z" }, + { url = "https://files.pythonhosted.org/packages/38/5f/793e8b6f4b6ac16e7d7198478c0af3670606fbb535c768d5f3e954781423/librt-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d1ddff067610a122387024c4df527493b909d41e54a6e5b2d0e6c1041d6dfa09", size = 148442, upload-time = "2026-06-30T16:12:24.582Z" }, + { url = "https://files.pythonhosted.org/packages/ad/92/c780fe37a9e0982f3bd8fd9a631d6b95d09a5a7201c6c50366ce843b7e42/librt-0.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8dc7ebb5f3eec062398e9d0ef1938acd21b589e74286c4a8906d0183318d91b", size = 478276, upload-time = "2026-06-30T16:12:26.101Z" }, + { url = "https://files.pythonhosted.org/packages/41/bb/226d444bc20d7dff4a19ec6c1ff2c13a76385eebddb59c9c00c923b67536/librt-0.12.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:198de569ea9d5f6f33808f1c00cc3db9de62bf4d6deafa3b052bd08255083038", size = 472337, upload-time = "2026-06-30T16:12:27.83Z" }, + { url = "https://files.pythonhosted.org/packages/12/79/98ac0840ee90a75d4e1155c79062860b12ccca508587ff2119fc086965f2/librt-0.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e958678a8bca56016aedc891b391c0e0813ea382a874b54a2c1b313c1d232720", size = 502087, upload-time = "2026-06-30T16:12:29.443Z" }, + { url = "https://files.pythonhosted.org/packages/6f/72/a6b1a0d080606a7f5f646b79a1496f21d709f8563877759ace9ce5adad73/librt-0.12.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575a6eca68c8437ed4a8e0f534e31d74b562ba1049a0ee4b5f09e114bcc21be1", size = 493202, upload-time = "2026-06-30T16:12:31.077Z" }, + { url = "https://files.pythonhosted.org/packages/69/cf/e1b036b45f2fc272205ee18bf272b47e8d684bf1a75af26db440c7504359/librt-0.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:86f241c50dc9e9a3f0db6dbb37a607c8205aa87b920802dabbd50b70d40f6939", size = 514139, upload-time = "2026-06-30T16:12:33.032Z" }, + { url = "https://files.pythonhosted.org/packages/40/34/b193b3e6985469a2f8afa86c90012329c86480b6ff4f2e4bd7b5b937e134/librt-0.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:113417b934fbf38220a9c7fe94578cefbe7dbb047adcb75aa197905af2b13724", size = 519486, upload-time = "2026-06-30T16:12:34.996Z" }, + { url = "https://files.pythonhosted.org/packages/31/9e/7de4947b1695f247c813f833e3c1e7b77b52e52a7dba2c35411cf806b58e/librt-0.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:762f17c0eb6b5d74e269126996cea8a89e35ab6464c5151619163abcd8623ae2", size = 499609, upload-time = "2026-06-30T16:12:36.663Z" }, + { url = "https://files.pythonhosted.org/packages/59/11/f3730e04e758b1fbf215359062ad2d5b6bd0b0ab5ac46b1c140628795be7/librt-0.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa93b3bd7f7588c628f6e9bf66485d3467fd9a1ccdb8975b770178f39f35697", size = 542205, upload-time = "2026-06-30T16:12:38.56Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8f/710453617eabe20e18433864f335534c8aff63fbc68d8cd9dbc70a3d08f6/librt-0.12.0-cp311-cp311-win32.whl", hash = "sha256:aaa04b44d4fe86d824616b1f9c13e34c7c01ec0c96dd2abc4f59423696f788e2", size = 98067, upload-time = "2026-06-30T16:12:40.102Z" }, + { url = "https://files.pythonhosted.org/packages/42/53/401bff50a56e95daf151d911c99adf5732af2190e8f4d11886c9a229103c/librt-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:9aaeeddb8e7e4ae3bb9f944e0e618418cb91c0071d5ddbfcc3584b3cf59d39f0", size = 118346, upload-time = "2026-06-30T16:12:41.388Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9a/a3a9078fe88bfc2d2d99dcf1c18593938ae830089cf84c3b2532a6c49d63/librt-0.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:18a2402fa3123ab76ecca670e6fb33038fde7c1e91181b885226ec4d30af2c2c", size = 104760, upload-time = "2026-06-30T16:12:43.112Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1a/5bec493821b0e85b91de4f234912b50133d1aedb875048eef27938ec3f96/librt-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9bce19aa7c05f91c989f9da7b567f81d21d57a2e6501e2b811aa0f3f79614c1a", size = 146756, upload-time = "2026-06-30T16:12:44.395Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d0/cc04b48a57c1f275387f5578847214c4a6c21bfb24c6c8c8d6ba753fe403/librt-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0ace09f5bf4d982fe726015f102fb856658b41580597104e301e630ed1d8d86", size = 145537, upload-time = "2026-06-30T16:12:45.95Z" }, + { url = "https://files.pythonhosted.org/packages/9e/10/c02325556beb2aa158c9e549ddade8cc9a23b36cdad14756dbed730c1ff1/librt-0.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d007efe9243ede81ce75990ad7aa172da1e2024144b3eff17ba46a5fff1fff3c", size = 488637, upload-time = "2026-06-30T16:12:47.658Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9e/7b49ca1c30baa9c8df96024aa09a97c35a97455e36004c9b5311703c56f3/librt-0.12.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:ad324a5e4858388a4864915b90a42efc8b374376393f14b9940f2454e791912b", size = 483651, upload-time = "2026-06-30T16:12:49.283Z" }, + { url = "https://files.pythonhosted.org/packages/4d/71/03c8c8cec39645fda451132ff9d6d662fc5aea42a1a188a77a4fddb35906/librt-0.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10a40cf74cdd97b6f8f905056db73f5d459783de2ca04c6ebd1bf47652818e7e", size = 518359, upload-time = "2026-06-30T16:12:50.999Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ec/a9f357f94bbcba92277d22af22cff42ef706ae5d9d6d58b69bebf3a67954/librt-0.12.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:92e61c09de95217ae02a9d17f4f66cf073253cdc51bcfdc0f15c62c9a70baa85", size = 509510, upload-time = "2026-06-30T16:12:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/7a/34/717055325d028743aa01a7691ad59a63352a26a8ff2e7eeb0c9249514150/librt-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0461344061d6fc3718940f5855d95647831cef6d03a6c7506897f98222784ad4", size = 527302, upload-time = "2026-06-30T16:12:54.244Z" }, + { url = "https://files.pythonhosted.org/packages/95/f8/7612eeedb3395d92f7c6a84dca5f15e282d650483a4dc01aa5b9cffdfda3/librt-0.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e6dfe89074732c9287b3c0f5a6af575c9ede380a788013876cc7b14fe0da0361", size = 532568, upload-time = "2026-06-30T16:12:55.74Z" }, + { url = "https://files.pythonhosted.org/packages/79/1e/a9afe85d5bb8b65dc27be3809ed1d69082079e1e9717fd2c66aa9939600c/librt-0.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9efed79d51ad1383bba0855f613cca7aa91c943e709af2413ac7f4bb9936ce08", size = 521579, upload-time = "2026-06-30T16:12:57.884Z" }, + { url = "https://files.pythonhosted.org/packages/b3/1e/93aebb219d52c37ea578f83b0588cd7b040974e464d4e435086a48b4dc4d/librt-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1eac6cc0e23e448fb3c1446ed85ff796afb616eed5897c978d35dbec030b7c7c", size = 558743, upload-time = "2026-06-30T16:12:59.577Z" }, + { url = "https://files.pythonhosted.org/packages/3c/85/1680c0ec332f238e3145c5608d313ab0a43281e210a5dd87e3bc3cc25631/librt-0.12.0-cp312-cp312-win32.whl", hash = "sha256:0ab8ee0210047ae86ca023ccfbfe3df82077fd1c9bc021aebbf37d993ef64af0", size = 99200, upload-time = "2026-06-30T16:13:01.015Z" }, + { url = "https://files.pythonhosted.org/packages/30/0e/abca12d8904875aa2ad66327390a3f7b1b75ebc43c0a00fc763cecf32ea5/librt-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:51c8bfa12632c81b94401c101bcedd0c56c3a1f8fa3273ca3472b28cd2f54003", size = 119390, upload-time = "2026-06-30T16:13:02.493Z" }, + { url = "https://files.pythonhosted.org/packages/32/a5/4203481b6d3a3bb348c82ac71abf1fcb4cb3ae8422a24a8dee4cd3ac5bd7/librt-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:5eebd451f5def089369ba6d8ff0291303d035e8154f9f26f7633835c5b029ade", size = 105117, upload-time = "2026-06-30T16:13:03.952Z" }, + { url = "https://files.pythonhosted.org/packages/f2/87/568d948c8079c9ff3c9e8110cf85f1eb70218e1209af29d0b7b89aa4a60c/librt-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8d9a55760a34ae5ce70434aabb6a6c61c6c44a0ec58ca1cfd9cd86e4745d417d", size = 146808, upload-time = "2026-06-30T16:13:05.417Z" }, + { url = "https://files.pythonhosted.org/packages/e7/1d/bea471ecea210088847bb5f3c4b4b424d596518934c06679b78ca85d6e63/librt-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ff0b197e338b4cf432873e0d6ef025213fdea85311ec4d87d2ea88c28adf2409", size = 145503, upload-time = "2026-06-30T16:13:07.023Z" }, + { url = "https://files.pythonhosted.org/packages/eb/9e/984ad422b56de95fdce158f06b051655373784ebea0aba9a7fcbc41614d1/librt-0.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e69f120a20b69e2539d603bbd4d62db38399b10f8bf73a1cf445038a621e8af", size = 488421, upload-time = "2026-06-30T16:13:08.492Z" }, + { url = "https://files.pythonhosted.org/packages/50/03/1a2f94009b07ea71f8e1a4cfe53370565b56da9caa341b89e0699325e9f5/librt-0.12.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:fde3cde595e947fc8e755b0a21f919a1622483d07c662d00496e040773d22591", size = 483488, upload-time = "2026-06-30T16:13:10.169Z" }, + { url = "https://files.pythonhosted.org/packages/aa/3b/084bdc295823fbb6ab91670047adf8f420787f9e8794bf2d140b66dc196b/librt-0.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d977447315fa09ea4e8c7ae9b4e22f7659b5128161c1fd55ff786b5349f73503", size = 518428, upload-time = "2026-06-30T16:13:11.681Z" }, + { url = "https://files.pythonhosted.org/packages/c9/22/5a307390b93a115ffbecd95c64eecb4e56269680e45e9415ada7285f2cf4/librt-0.12.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ffac8a67e4143cea9a549d4822b93bc0bbaad73fc25aa0ab0ba5ec27d178677", size = 509744, upload-time = "2026-06-30T16:13:13.217Z" }, + { url = "https://files.pythonhosted.org/packages/b5/90/83f3cb6184f5d669660717b4b2e317c9ddaccf7ca5bb97f2196deac1a3b7/librt-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:94af1ed773ff104ef08ef3d669a0ba9d3a5916c609eb698cffe5d5476d66ff9b", size = 527749, upload-time = "2026-06-30T16:13:15.277Z" }, + { url = "https://files.pythonhosted.org/packages/7d/3b/f162be5cc88d47378e3a20776fe425fa1c2bece755da15e2783ebf06d3d6/librt-0.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:548199d21d22fb26398dfbbe0ba953a52465c66f3a49f38e6fddce1b127faf53", size = 532582, upload-time = "2026-06-30T16:13:17.074Z" }, + { url = "https://files.pythonhosted.org/packages/c9/28/6c5d2f6b7232fd24f284fc4cab37a459fe69a9096a09942f44cc5c55e073/librt-0.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c8f1f413b966a9dd3ecf80cd337b0ad7bb3de2474a4ff448ed3ebabfc3f803fc", size = 522235, upload-time = "2026-06-30T16:13:18.823Z" }, + { url = "https://files.pythonhosted.org/packages/a9/1c/bd115360587fdc22c8ae8fac14c040a556b442e2965d4370d2cf274c8b95/librt-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:55f13f95b629be5b6ab38918e439bf14169d6f9a8deaae55e0c14e12fb0c74b9", size = 559055, upload-time = "2026-06-30T16:13:20.509Z" }, + { url = "https://files.pythonhosted.org/packages/fe/5a/c26f49f576437014825a86faea3cec60c1ed17f976abd567b6c12b8e35a7/librt-0.12.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:8b2dc079dfe29e77a47a19073d2040fa4879aa3656501f1650f8402ddce0313c", size = 79809, upload-time = "2026-06-30T16:13:22.401Z" }, + { url = "https://files.pythonhosted.org/packages/69/0b/a55244261d9ad7375ac039b8af06d42602722e2e8b8d8d6b86e4a3888c02/librt-0.12.0-cp313-cp313-win32.whl", hash = "sha256:da58944be8270f2bfee628a9a2a60c1cf6a12c8bea8e2c9b6edf3e5414ca7793", size = 99308, upload-time = "2026-06-30T16:13:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/c9/bf/ed9465e58d44c5a5637795547d0841c8934aab905ea452cac1adf14672cf/librt-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:1db4be3037e4ce065a071fa7deee93e78ebc25f448340a02a6c1c0b82c37e383", size = 119438, upload-time = "2026-06-30T16:13:25.188Z" }, + { url = "https://files.pythonhosted.org/packages/c0/44/3cad652aeb892e6e8ffe48d0fafa2bc652f28ec7ed3f4403fcbb1be4f948/librt-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:05fd2542892ad770b5dd45003fd080477cf220b611d3ee59b0792097eb0873a9", size = 105118, upload-time = "2026-06-30T16:13:26.533Z" }, + { url = "https://files.pythonhosted.org/packages/0e/51/3a0e05618c12423b6fc5141b590ec02a6efb645833edc8736a6c7b46d1ec/librt-0.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:b37ee42e09722284a6d9288fe44a191f7276060a3195939bb77c6502058dbb34", size = 145579, upload-time = "2026-06-30T16:13:27.909Z" }, + { url = "https://files.pythonhosted.org/packages/77/9e/fd399d099dfb4020f3f7c34e7e6210c389fa89f7d79ca92f5afb0395f278/librt-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ade11988728b3e4768dadc5696e82c60e9b35fc95335a9b4d1f5d69e753ccec7", size = 150139, upload-time = "2026-06-30T16:13:29.357Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ee/610239fbd8c4b005443664c5d4c3bc1717daedd8c71369bf45011aa87194/librt-0.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f351ed425380e39bd86df382578aa5b8c5b98e2e265112de7379e7d030258150", size = 480457, upload-time = "2026-06-30T16:13:30.78Z" }, + { url = "https://files.pythonhosted.org/packages/0c/10/ceddc9010f26c541444be36e1153a79b64626694db2d33a524c719fa3e46/librt-0.12.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:857d2163e088c868967717ace8e980017fd868a735f3de010412af02bdc30319", size = 479002, upload-time = "2026-06-30T16:13:32.398Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f1/b1523d9718e8192e5403e6b41a02742e17ba554369f0729b9f30ab590e2d/librt-0.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2befc80aa5f2f5b93f28abaaf11feff6677931dd548320e44c52deaa9399744", size = 510527, upload-time = "2026-06-30T16:13:34.615Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0e/0f3ff43befb18a531615736791e52fb67eaa71ff7b89e6e5f7004b64cc6e/librt-0.12.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:be3694dcfa97c6715dd19ac73d3e1b21a805514a5785663e57fecacd3ff64e5a", size = 500988, upload-time = "2026-06-30T16:13:36.408Z" }, + { url = "https://files.pythonhosted.org/packages/a8/1a/0278ea4a9e599dc507c43839a87f2c764ad04bf69418e2d763d58659e55f/librt-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2d5f67e86f45638843d025b0828f2e9e55fc45ff9180d2618ccdeaf72a796050", size = 519318, upload-time = "2026-06-30T16:13:37.883Z" }, + { url = "https://files.pythonhosted.org/packages/59/55/090e10e62be2f35265e41601337f83ac9f83be9aca1bf92692e3a82effdd/librt-0.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:64572c85e4ab7d572c9b72cd76b5f90b21181b1459fa6b1aac6f8958c4fcff31", size = 527127, upload-time = "2026-06-30T16:13:39.682Z" }, + { url = "https://files.pythonhosted.org/packages/1f/34/8052c9ec678be6ba751279947831f089aa69b009000b985ce91d1979669a/librt-0.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:8b961912b0e688c1eb4658a46bdb0606b31918d65597fbe7356ca83aa653ffcc", size = 509766, upload-time = "2026-06-30T16:13:41.266Z" }, + { url = "https://files.pythonhosted.org/packages/6f/f8/8761b36189e9ec8dc20b49fa84cef22852c6c41fcda56f760f7fc1360da5/librt-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:722375903e3f079436a7a33da51ce73931536dd041f9feb01536f05d8e010c96", size = 552043, upload-time = "2026-06-30T16:13:43.197Z" }, + { url = "https://files.pythonhosted.org/packages/c8/98/7283971ef6b70269938b49c7b25f670ec6325d252265fbcc996f9b364379/librt-0.12.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:a5a96a8f536b65ef1bf910c09e7e71647edde5111f6e1b51f413c6fba5bfe71b", size = 79472, upload-time = "2026-06-30T16:13:44.64Z" }, + { url = "https://files.pythonhosted.org/packages/c3/5e/b30940dea935e8ac5bd0e0abb1985f5274590d557ac3a252ca0d5392ce52/librt-0.12.0-cp314-cp314-win32.whl", hash = "sha256:8ffc99c356f1777c506e1b69dc303879153ae2640ba15b8f3d4448bc87139149", size = 94246, upload-time = "2026-06-30T16:13:45.962Z" }, + { url = "https://files.pythonhosted.org/packages/7d/4e/0af9fe63f35fa304da3b05688f30ff6a329bcc59581b1cc51dc87fd30141/librt-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:1e68fb20798f455cda41d20a306a23c901218883f17a4bab1ed6e1331b265fb7", size = 114951, upload-time = "2026-06-30T16:13:47.279Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8e/843c495d7db35e13b84cd533898fa89145c40dc255da0bc316d53d631464/librt-0.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:2df534f97916cf38ec9b1ddafeb68ae1a4cd4a54775ff26a797026774c0517cf", size = 100562, upload-time = "2026-06-30T16:13:48.699Z" }, + { url = "https://files.pythonhosted.org/packages/75/30/c686d0f978d5fd6867c5bbad96b015c9445746764d1c228e16a2d30d9382/librt-0.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c09e581b1c2b8a62b809d4f4bd101ca3de93791e5b0ed1a14085d911be3dee3f", size = 153897, upload-time = "2026-06-30T16:13:50.017Z" }, + { url = "https://files.pythonhosted.org/packages/40/46/f6f2d77ce46628b48fb5280709013b5109cf3a2c46a2472093cdfc03519d/librt-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:976888d0d831402086e641018bcc3208e0a38f0835789da91f72894b2cb4161f", size = 156391, upload-time = "2026-06-30T16:13:51.462Z" }, + { url = "https://files.pythonhosted.org/packages/c2/46/cd790c7e19e460779471530ffab454541d6ea4a3b7d338cad7f16ff96995/librt-0.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:563c37cdb41d08fe1e3f08b201abac0e317ca18e88b91285466ee0a585797520", size = 564151, upload-time = "2026-06-30T16:13:53.146Z" }, + { url = "https://files.pythonhosted.org/packages/54/12/724559a15fb023cbdef7aee1e81fbfbc3ee22fd09009baa816cea63e3a60/librt-0.12.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:b97eb1a3140e279cc76f85b0fb92b7eb3dfbe0471260ee878bc9dc4bf9a0d649", size = 546002, upload-time = "2026-06-30T16:13:54.665Z" }, + { url = "https://files.pythonhosted.org/packages/4b/7e/f9d8c257ab4909f101c7c13734367749e782fd8625545f0343502c2f09f1/librt-0.12.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06e0623351ab9904cf628245f99c714586f4dd23dc740b88c8bc670d8401a847", size = 584204, upload-time = "2026-06-30T16:13:56.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/33/64665810575ac23b6cb6ef364de51309b7803620c12885b6e895ebc29591/librt-0.12.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da12f017b2e404554be14d466cd992459feaa44f252b0f18d909a85266ce1237", size = 573688, upload-time = "2026-06-30T16:13:58.1Z" }, + { url = "https://files.pythonhosted.org/packages/0f/01/27522995c6627455abc7a939d57535fb1a7836d398ccedb3d7585f46039e/librt-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d97f31003a5c86b9e78155a829572c3a26484064fb7ac1d9695fe628bd93d029", size = 604719, upload-time = "2026-06-30T16:13:59.831Z" }, + { url = "https://files.pythonhosted.org/packages/ee/1f/099e61b1b688551d6d2ce9d4d2ae2242a938759db8551e6cbac7f7176ee5/librt-0.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:bd43a6c69876aef4f04eaae3d3b99b0be64755fda274002fa445b92480bf664e", size = 598183, upload-time = "2026-06-30T16:14:01.457Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c1/050400249665503bdd5b83cec518fa7b183b609341c8dcd58161775c4226/librt-0.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c01755c72fca1dc6b8d5c2ed228b8e7b2ffe184675c22f0f05ebd8fe188b9250", size = 582559, upload-time = "2026-06-30T16:14:03.29Z" }, + { url = "https://files.pythonhosted.org/packages/da/d1/eef8f0e6722518b65a3d3bcd9309f9f44e208ce5d6728070820f988e7078/librt-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:625ae561d5fa36400856dcc27464400d047bc2d5e3446be88f437b03fefd72e4", size = 626375, upload-time = "2026-06-30T16:14:04.957Z" }, + { url = "https://files.pythonhosted.org/packages/8b/78/f0bb41a6f2bbd3c77bdcc66980dc0d69ca1192a0ecec25377afcc5e6db73/librt-0.12.0-cp314-cp314t-win32.whl", hash = "sha256:8d73191883553ee0739741544bf3b00aba2a1224e45d9580b30cbc29e21dc03b", size = 97752, upload-time = "2026-06-30T16:14:06.555Z" }, + { url = "https://files.pythonhosted.org/packages/92/24/e279c27972ab051a070237cfa45728fa51670c3f22f1a4d391711e9f4c31/librt-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e1cbb037324e759f0afa270229731ff0047772667f3cb38ef5df2cabf0175ede", size = 119562, upload-time = "2026-06-30T16:14:07.908Z" }, + { url = "https://files.pythonhosted.org/packages/06/e6/42a475bfca683b0cd5366f6dd06580062b7e567bb8534d225c877c2f14f3/librt-0.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:bca1472acbd473eff61059b4409f802c5a1bcb4cd0344d06f939df9c4c125d40", size = 104282, upload-time = "2026-06-30T16:14:09.29Z" }, +] + +[[package]] +name = "mypy" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ast-serialize" }, + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/a1/639f3024794a2a15899cb90707fe02e044c4412794c39c5769fd3df2e2ef/mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41", size = 14691685, upload-time = "2026-05-11T18:33:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/3b/08/9a585dea4325f20d8b80dc78623fa50d1fd2173b710f6237afd6ba6ab39b/mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca", size = 13555165, upload-time = "2026-05-11T18:32:16.107Z" }, + { url = "https://files.pythonhosted.org/packages/81/dc/7c42cc9c6cb01e8eb09961f1f738741d3e9c7e9d5c5b30ec69222625cd5f/mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538", size = 13994376, upload-time = "2026-05-11T18:32:39.256Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fa/285946c33bce716e082c11dfeee9ee196eaf1f5042efb3581a31f9f205e4/mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398", size = 14864618, upload-time = "2026-05-11T18:34:49.765Z" }, + { url = "https://files.pythonhosted.org/packages/2b/83/82397f48af6c27e295d57979ded8490c9829040152cf7571b2f026aeb9a0/mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563", size = 15102063, upload-time = "2026-05-11T18:34:05.855Z" }, + { url = "https://files.pythonhosted.org/packages/40/68/b02dec39057b88eb03dc0aa854732e26e8361f34f9d0e20c7614967d1eba/mypy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389", size = 11060564, upload-time = "2026-05-11T18:35:36.494Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a8/ea3dcbef31f99b634f2ee23bb0321cbc8c1b388b76a861eb849f13c347dc/mypy-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666", size = 9966983, upload-time = "2026-05-11T18:37:14.139Z" }, + { url = "https://files.pythonhosted.org/packages/95/b1/55861beb5c339b44f9a2ba92df9e2cb1eeb4ae1eee674cdf7772c797778b/mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af", size = 14874381, upload-time = "2026-05-11T18:37:31.784Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b3/b7f770114b7d0ac92d0f76e8d93c2780844a70488a90e91821927850da86/mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6", size = 13665501, upload-time = "2026-05-11T18:34:23.063Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f3/8ae2037967e2126689a0c11d99e2b707134a565191e92c60ca2572aec60a/mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211", size = 14045750, upload-time = "2026-05-11T18:31:48.151Z" }, + { url = "https://files.pythonhosted.org/packages/a0/32/615eb5911859e43d054941b0d0a7d06cfa2870eba86529cf385b052b111c/mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b", size = 15061630, upload-time = "2026-05-11T18:37:06.898Z" }, + { url = "https://files.pythonhosted.org/packages/d4/03/4eafbfff8bfab1b87082741eae6e6a624028c984e6708b73bce2a8570c9d/mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22", size = 15288831, upload-time = "2026-05-11T18:31:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/919661478e5891a3c96e549c036e467e64563ab85995b10c53c8358e16a3/mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b", size = 11135228, upload-time = "2026-05-11T18:34:31.23Z" }, + { url = "https://files.pythonhosted.org/packages/24/0a/6a12b9782ca0831a553192f351679f4548abc9d19a7cc93bb7feb02084c7/mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8", size = 10040684, upload-time = "2026-05-11T18:36:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/6e/dd/c7191469c777f07689c032a8f7326e393ea34c92d6d76eb7ce5ba57ea66d/mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5", size = 14852174, upload-time = "2026-05-11T18:31:38.929Z" }, + { url = "https://files.pythonhosted.org/packages/55/8c/aed55408879043d72bb9135f4d0d19a02b886dd569631e113e3d2706cb8d/mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e", size = 13651542, upload-time = "2026-05-11T18:36:04.636Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8e/f371a824b1f1fa8ea6e3dbb8703d232977d572be2329554a3bc4d960302f/mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e", size = 14033929, upload-time = "2026-05-11T18:35:55.742Z" }, + { url = "https://files.pythonhosted.org/packages/94/21/f54be870d6dd53a82c674407e0f8eed7174b05ec78d42e5abd7b42e84fd5/mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285", size = 15039200, upload-time = "2026-05-11T18:33:10.281Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/bf21748626a40ce59fd29a39386ab46afec88b7bd2f0fa6c3a97c995523f/mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5", size = 15272690, upload-time = "2026-05-11T18:32:07.205Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d7/9e90d2cf47100bea550ed2bc7b0d4de3a62181d84d5e37da0003e8462637/mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65", size = 11147435, upload-time = "2026-05-11T18:33:56.477Z" }, + { url = "https://files.pythonhosted.org/packages/ec/46/e5c449e858798e35ffc90946282a27c62a77be743fe17480e4977374eb91/mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d", size = 10035052, upload-time = "2026-05-11T18:32:30.049Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ca/b279a672e874aedd5498ae25f722dacc8aa86bbffb939b3f97cbb1cf6686/mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2", size = 14848422, upload-time = "2026-05-11T18:35:45.984Z" }, + { url = "https://files.pythonhosted.org/packages/27/e6/3efe56c631d959b9b4454e208b0ac4b7f4f58b404c89f8bec7b49efdfc21/mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f", size = 13677374, upload-time = "2026-05-11T18:36:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/84/7f/8107ea87a44fd1f1b59882442f033c9c3488c127201b1d1d15f1cbd6022e/mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4", size = 14055743, upload-time = "2026-05-11T18:35:18.361Z" }, + { url = "https://files.pythonhosted.org/packages/51/4d/b6d34db183133b83761b9199a82d31557cdbb70a380d8c3b3438e11882a3/mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef", size = 15020937, upload-time = "2026-05-11T18:34:59.618Z" }, + { url = "https://files.pythonhosted.org/packages/ff/d7/f08360c691d758acb02f45022c34d98b92892f4ea756644e1000d4b9f3d8/mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135", size = 15253371, upload-time = "2026-05-11T18:36:41.081Z" }, + { url = "https://files.pythonhosted.org/packages/67/1b/09460a13719530a19bce27bd3bc8449e83569dd2ba7faf51c9c3c30c0b61/mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21", size = 11326429, upload-time = "2026-05-11T18:34:13.526Z" }, + { url = "https://files.pythonhosted.org/packages/40/62/75dbf0f82f7b6680340efc614af29dd0b3c17b8a4f1cd09b8bd2fd6bc814/mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57", size = 10218799, upload-time = "2026-05-11T18:32:23.491Z" }, + { url = "https://files.pythonhosted.org/packages/b2/66/caca04ed7d972fb6eb6dd1ccd6df1de5c38fae8c5b3dc1c4e8e0d85ee6b9/mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e", size = 15923458, upload-time = "2026-05-11T18:35:28.64Z" }, + { url = "https://files.pythonhosted.org/packages/ed/52/2d90cbe49d014b13ed7ff337930c30bad35893fe38a1e4641e756bb62191/mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780", size = 14757697, upload-time = "2026-05-11T18:36:14.208Z" }, + { url = "https://files.pythonhosted.org/packages/ac/37/d98f4a14e081b238992d0ed96b6d39c7cc0148c9699eb71eaa68629665ea/mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd", size = 15405638, upload-time = "2026-05-11T18:33:48.249Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c2/15c46613b24a84fad2aea1248bf9619b99c2767ae9071fe224c179a0b7d4/mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08", size = 16215852, upload-time = "2026-05-11T18:32:50.296Z" }, + { url = "https://files.pythonhosted.org/packages/5c/90/9c16a57f482c76d25f6379762b56bbf65c711d8158cf271fb2802cfb0640/mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081", size = 16452695, upload-time = "2026-05-11T18:33:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/0f/4c/215a4eeb63cacc5f17f516691ea7285d11e249802b942476bff15922a314/mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7", size = 12866622, upload-time = "2026-05-11T18:34:39.945Z" }, + { url = "https://files.pythonhosted.org/packages/4b/50/1043e1db5f455ffe4c9ab22747cd8ca2bc492b1e4f4e21b130a44ee2b217/mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6", size = 10610798, upload-time = "2026-05-11T18:36:31.444Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.12' and sys_platform == 'win32'", + "python_full_version < '3.12' and sys_platform == 'emscripten'", + "python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/05/3d27272d30698dc0ecb7fdfaa41ad70303b444f81722bb99bce1d818638a/numpy-2.5.0.tar.gz", hash = "sha256:5a129578019311b6e56bdd714250f19b518f7dceeeb8d1af5490f4942d3f891c", size = 20652461, upload-time = "2026-06-21T20:57:51.95Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/0a/11486d02add7b1384dff7374d124b1cfbb0ee864dcc9f6a2c0380638cf84/numpy-2.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:489780423903667933b4ed6197b6ec3b75ea5dd17d1d8f0f38d798feb6921561", size = 16789987, upload-time = "2026-06-21T20:56:16.657Z" }, + { url = "https://files.pythonhosted.org/packages/55/b2/285f48640a181947b4587a3766d21ec1eaa7fea833d4b49957e09da467a2/numpy-2.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ece55976ced6bca95a03ae2839e2e5ccffe8eb6a3e7022415645eb154a81e4e6", size = 11760322, upload-time = "2026-06-21T20:56:19.813Z" }, + { url = "https://files.pythonhosted.org/packages/dd/67/b032db1eb03ca30d16eda3b0c22aaa615338b9263c2fd559d0f29451aca4/numpy-2.5.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:c83b664b0e6eee9594fa920cf0639d8af796606d3fad6cc70180c87e4b97c7be", size = 5319605, upload-time = "2026-06-21T20:56:22.173Z" }, + { url = "https://files.pythonhosted.org/packages/b9/83/03fc7300c7c6b6c84c487b1dc80d322817b95fbd1f4dd57a85e23b7198de/numpy-2.5.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:bf80333980bf37f523341ddd72c783f39d6829ec7736b9eb99086388a2d52cc2", size = 6653628, upload-time = "2026-06-21T20:56:23.914Z" }, + { url = "https://files.pythonhosted.org/packages/82/49/2ec21730bc63ccfda829323f7040a8ed4715b3852ce658689cf74ee96a8c/numpy-2.5.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1a4874217b36d5ac8fc876f52e39df56f8182c88463e9e2dceabf7ca8b7efb8", size = 15153691, upload-time = "2026-06-21T20:56:25.631Z" }, + { url = "https://files.pythonhosted.org/packages/bb/6b/f4a3d0637692c49da8ef99d72d52526f92e0a8d6ac4f0ca9f31441b9d9ea/numpy-2.5.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aaa760137137e8d3c920d27927748215b56014f92667dc9b6c27dfc61249255a", size = 16660066, upload-time = "2026-06-21T20:56:28.009Z" }, + { url = "https://files.pythonhosted.org/packages/3a/2f/c354ec86d1f3f5c19649463b0d39652e160736e5b0a4cd18dff0576715c4/numpy-2.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7174ce8265fc7f7417d171c9ea8fe905220748893ea67a2a7abe726ec331c4b0", size = 16514638, upload-time = "2026-06-21T20:56:30.26Z" }, + { url = "https://files.pythonhosted.org/packages/06/34/43efdcb319988648580f93c11f1ae82cf7e2faa74925e98e454ae3aa95f8/numpy-2.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b8c3daaf99de52415d20b42f8e8155c78642cb04207d02f9d317a0dcf1b3fb54", size = 18419647, upload-time = "2026-06-21T20:56:32.41Z" }, + { url = "https://files.pythonhosted.org/packages/71/e2/f5d1676b1d7fb682eb5e9a1641e7ebd2414b3216c370661d1029778908b4/numpy-2.5.0-cp312-cp312-win32.whl", hash = "sha256:6206db0af545d73d068add6d992279145f158428d1da6cc49adc4b630c5d6ee5", size = 6056688, upload-time = "2026-06-21T20:56:34.657Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7c/48f115d1c58a34032facebcd51fdf2d02df2c51d4a46a81dd1197bb2ea6b/numpy-2.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:6f2d6873e2940c860a309d21e25b1e69af6aaffdd80aa056b04c16380db1c4f2", size = 12419237, upload-time = "2026-06-21T20:56:36.24Z" }, + { url = "https://files.pythonhosted.org/packages/86/26/2e0882f4044d1b1a1b63e875151fb2393389032022a8b7f5657a7996d3b2/numpy-2.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:a55e1eb2bca2cfd17a16b213c99dfc8502d47b0d494224d2122277d0400935ca", size = 10339912, upload-time = "2026-06-21T20:56:38.733Z" }, + { url = "https://files.pythonhosted.org/packages/8a/33/07675aaad7f26ea013d5e884d9a0d784b79c6bd7566c333f5a52fa3c610b/numpy-2.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:520e6b8be0a4b65840ac8090d4f51cef4bed66e2b0894d5a520f099adc24a9b2", size = 16784890, upload-time = "2026-06-21T20:56:40.799Z" }, + { url = "https://files.pythonhosted.org/packages/85/4b/953118a730ee3b35e28645e0eb4cf9beec5bdbb954e1ac2f5fcefba6bbc3/numpy-2.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:146b81cdd3967fdb6beca8ba25f00c58741d8f3cbd797f55af0fbe0bfec3469c", size = 11754584, upload-time = "2026-06-21T20:56:43.094Z" }, + { url = "https://files.pythonhosted.org/packages/44/9b/56dd530c367c74ae17411027cea4135ca57e1e0583bf5594cee18bd83217/numpy-2.5.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:126b88d95e8ff9b00c9e717aa540469f21d6180162f84c0caec51b16215d49cd", size = 5313904, upload-time = "2026-06-21T20:56:45.503Z" }, + { url = "https://files.pythonhosted.org/packages/ce/b0/bcd672edad27ecca7da1f7bb0ce72cd1706a4f2d79ae94990afc97c13e1c/numpy-2.5.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d4313cef1594c5ce46c31b6e54e918338f63f16ee9322304e8c9114d6d81c8bd", size = 6648504, upload-time = "2026-06-21T20:56:47.567Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/15cdfcbd30a1544a46c9e487a00df331c4672450216538705a9e51fa6710/numpy-2.5.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:750fb097caf26fa878746d9d119f6f9da12dedcbff1eea966c3e3447647c4a9e", size = 15150086, upload-time = "2026-06-21T20:56:49.352Z" }, + { url = "https://files.pythonhosted.org/packages/32/4e/8d7656ccaab3e81e97258b8a9bc5f0c8502513a92fb4ceb0a2cbfebc17bf/numpy-2.5.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3893adc2dc7c0412ba76777db55a049215d99c9aa3113003be8f49f4f1290ab9", size = 16647250, upload-time = "2026-06-21T20:56:51.542Z" }, + { url = "https://files.pythonhosted.org/packages/3c/81/97060281b602ed07f21b12f4ec409eac1f75a2f91fbc829ed8b2becf3ad4/numpy-2.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:835e454dd99b238cdc5a3f63bce2371296f5ebc53ca1e0f8e6ddbb6d92a29aab", size = 16512864, upload-time = "2026-06-21T20:56:55.401Z" }, + { url = "https://files.pythonhosted.org/packages/33/ab/4496208146911f8d8ddb54f68a972aafa6c8d44babcb2ea03b0e5cc87c9d/numpy-2.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f9836778081a0a3c02a6a21493f3e9f5b311f8d2541934f31f05583dc999ea4", size = 18408407, upload-time = "2026-06-21T20:56:57.75Z" }, + { url = "https://files.pythonhosted.org/packages/d4/9f/a4df67c181e4ee8b467aa3332dc2db10fd5c515136831302f3ca48bc0a01/numpy-2.5.0-cp313-cp313-win32.whl", hash = "sha256:0b525be4744b60bb0557ac872d53ef07d085b5f39622bc579c98d3809d05b988", size = 6054431, upload-time = "2026-06-21T20:57:00.016Z" }, + { url = "https://files.pythonhosted.org/packages/30/53/491e1c47c55b62ccc6a63c1c5b8635c73fc2258dddeb9bda27cae4a0ae96/numpy-2.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:44353e2878930039db472b99dc353d749826e4010bd4d2a7f835e94a97a5c748", size = 12414420, upload-time = "2026-06-21T20:57:01.815Z" }, + { url = "https://files.pythonhosted.org/packages/eb/4a/25c2906f541e9d9f4c5769764db732e6627be91a13f4724fa10634d77db4/numpy-2.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:48f54b00711f83a5f796b70c518e8c2b3c5848dda03a54911f23eb68519b9b60", size = 10339533, upload-time = "2026-06-21T20:57:03.961Z" }, + { url = "https://files.pythonhosted.org/packages/86/ad/abc44aaceaf7b17ee1edde2bbb4458da591bc79574cffff50c4bb35f00d1/numpy-2.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f27582c55ba4c750b7c58c8faf021d2cd9324a662b466229db8a417b41368af9", size = 16783807, upload-time = "2026-06-21T20:57:06.253Z" }, + { url = "https://files.pythonhosted.org/packages/5d/39/b72e168daf9c00fb20c9fc996d00437ccecdef3102387775d29d7a62576d/numpy-2.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:28e7137057d551e4a83c4ae414e3451f50568409db7569aacc7f9811ee06a446", size = 11765215, upload-time = "2026-06-21T20:57:08.547Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a0/8400a9c0e3625182347593f5e1f57da9a617a534794805c8df5518154ddc/numpy-2.5.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e1da54b53e75cd9fcfc23efcc7edab2c6aecf97b6037566d8a0fe804af8ec57c", size = 5324493, upload-time = "2026-06-21T20:57:11.012Z" }, + { url = "https://files.pythonhosted.org/packages/f6/8c/0d104deaa0401c93395a629ec902891618a2eff76d19229139cb5a887bfc/numpy-2.5.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:694d8f74e156f7fd01179f1aa8faa2f648ab6ae0f70b6c3fe57a03249aea2303", size = 6645211, upload-time = "2026-06-21T20:57:12.919Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d9/4a4a628c812750363786afc3d33492709a5cd64b215469c16b0f6c7bb811/numpy-2.5.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1a7569a7b53c77716f036bb28cb1c91f166a26ec7d9502cd1e4bdfe502fdec22", size = 15166004, upload-time = "2026-06-21T20:57:14.717Z" }, + { url = "https://files.pythonhosted.org/packages/a0/5e/2a902317d7fc4aa93236e80c932662dadfc459b323d758329e01775125e1/numpy-2.5.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:39a0433bd4086ebd462960cf375e19195bb07b53dc1d87dd5fcf47ad78576f03", size = 16650797, upload-time = "2026-06-21T20:57:16.906Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a0/a0090e6329f4ca5992c07847bb579c5259a19953dc57255bb08793142ffb/numpy-2.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:929f0c79ac38bcbd7154fe631dc907abfeddbcc5027a896bd1f7767323271e7a", size = 16524647, upload-time = "2026-06-21T20:57:19.165Z" }, + { url = "https://files.pythonhosted.org/packages/5e/7d/6caf27734c42b65837e7461ed0dbbd6b6fc835060c9714ec59d673bb383a/numpy-2.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cc4f247a47bbf070bfd70be53ccdcf47b800af563535e7bbe172322197c30e21", size = 18411841, upload-time = "2026-06-21T20:57:21.638Z" }, + { url = "https://files.pythonhosted.org/packages/13/dc/26edadbd812536769a82c2e9e002234e33feb5da43061d47a044f6d309b7/numpy-2.5.0-cp314-cp314-win32.whl", hash = "sha256:5dc71423499fab3f46f7a7201155ade1669ea101f2f429d332df9e72f8161731", size = 6106361, upload-time = "2026-06-21T20:57:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/f2/9e/4dd1459282229a72d92dece2ae9138e5cac94a72263a7ceb48f37434c925/numpy-2.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:ebb81d9d5443e0309d6c54894c3fbed74ad7da0714352a67b6d773cd189eae73", size = 12551749, upload-time = "2026-06-21T20:57:25.945Z" }, + { url = "https://files.pythonhosted.org/packages/05/a7/6bc6384c080b86c7f6c85c5bc5b540b24f4f679cd144791d99574e90d462/numpy-2.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:3b94d0d0deceebfad3e67ae5c0e5eb87371e8f7a0581cd04a779928c2450cf1e", size = 10617072, upload-time = "2026-06-21T20:57:28.175Z" }, + { url = "https://files.pythonhosted.org/packages/86/6b/4a2b71d66ada5608ae02b63f150dfad520f6940721cb7f029ad270befc0e/numpy-2.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:22f3d43e362d650bc39db1f17851302874a148ca95ba6981c1dfb5fa6862f35b", size = 11881067, upload-time = "2026-06-21T20:57:30.104Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b2/d365eb40a20efb49d67e9feb90494ed8511282ee1f5fa16006675c65397d/numpy-2.5.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:243563efb4cd7528a264567e9fd206c87826457322521d06206a00bfa316c927", size = 5440290, upload-time = "2026-06-21T20:57:32.193Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5e/e9c03188de5f9b767e46a8fe988bcfd3efad066a4a3fda8b9cb11a93f895/numpy-2.5.0-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:84881d825ca75249b189bbee875fcfe3238aa5c479e6100893cda566e8e86826", size = 6748371, upload-time = "2026-06-21T20:57:33.933Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1d/68c186a38a5027bae2c4ddd5ea681fdaf8b4d30fb7301def6d8ad270390f/numpy-2.5.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cda12aa4779d42b8771180aba759c96f527d43446d8f380ab59e2b35e8489efd", size = 15214643, upload-time = "2026-06-21T20:57:35.677Z" }, + { url = "https://files.pythonhosted.org/packages/8c/67/73f67b7c7e20635baae9c4c3ead4ae7326a005900297a6110971abd62eb5/numpy-2.5.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c0121101093d2bd74981b10f8837d78e794a8ff57834eb27179f49e1ba11ac6", size = 16690128, upload-time = "2026-06-21T20:57:38.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/05/d4c1fb0c46d02a27d6b2b8b319a78c90937acec8631c1641874670b31e6f/numpy-2.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d371c92cfa09da00022f501ab67fafaea813d752eb30ac44336d45b1e5b0268a", size = 16577902, upload-time = "2026-06-21T20:57:40.447Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1d/771c797d50fa26e4888989cccf1d50ee51f530d4e455ad2692dcb64fa711/numpy-2.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9990713e9c38154c6861e7547f1e3fc7a87e75ff09bab24ef1cc81d81c2835e9", size = 18452814, upload-time = "2026-06-21T20:57:42.875Z" }, + { url = "https://files.pythonhosted.org/packages/e8/46/52fc0d2a68d7643f0f149eeea5a5d8ea2a3507056ac8afa83c9212606e8b/numpy-2.5.0-cp314-cp314t-win32.whl", hash = "sha256:edadfbd4794b1086c0d822f81863e8a68fc129d132fd0bb9e31e955d7fbbbdb7", size = 6253168, upload-time = "2026-06-21T20:57:45.101Z" }, + { url = "https://files.pythonhosted.org/packages/2a/be/6c8d1118b5f13b2881dc095d5b345de19c6638b8959c17409b6eff84c8aa/numpy-2.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f7e5fa4382967ae6548bd2f174219afb908e294b0d5f625af01166edd5f7d9aa", size = 12736286, upload-time = "2026-06-21T20:57:46.935Z" }, + { url = "https://files.pythonhosted.org/packages/fd/6a/d3a169aaf8536cf228d56a09e04bcb713a2fe4410d4e2105b9419b5a9c89/numpy-2.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:016623417bb330d719d579daf2d6b9a01ddc52e41a9ed61a47f39fde46dcd865", size = 10686451, upload-time = "2026-06-21T20:57:49.313Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/16/b5c76b838fd9bf6ce84d3a53346b8874ec05c5f0040d75ef2c320100cd2a/pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98", size = 10338495, upload-time = "2026-05-11T18:52:11.558Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b0/a4ffc4ae74d2d822200dcc46898987d8eb6032d1e2b219cae39da6f5cbcc/pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639", size = 9938250, upload-time = "2026-05-11T18:52:17.005Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b2/3323601a52caee42c019e370090ca4544b241437240ca04f786cce82b0cf/pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2", size = 10770558, upload-time = "2026-05-11T18:52:19.865Z" }, + { url = "https://files.pythonhosted.org/packages/32/f1/bbecd2f867b97abebe0f9b53d750f862251b40337e061b36676ded3d920f/pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27", size = 11274611, upload-time = "2026-05-11T18:52:22.622Z" }, + { url = "https://files.pythonhosted.org/packages/7f/4f/eafabf2d5fae5adf143b4d18d3706c5efdc368a7c4eb1ee8a3eddabbd0f6/pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824", size = 11784670, upload-time = "2026-05-11T18:52:25.4Z" }, + { url = "https://files.pythonhosted.org/packages/49/44/1eb20389301b57b19cc099a1c2f662501f72f08a65f912d05822613c1532/pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938", size = 12353708, upload-time = "2026-05-11T18:52:28.139Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/c321f13b5ba1819fc8dca456c7fce578da2dcfecff1abbf0eaddf8406c0f/pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea", size = 9907609, upload-time = "2026-05-11T18:52:30.982Z" }, + { url = "https://files.pythonhosted.org/packages/53/85/1b7f563ebc6357c27233a02a96b589bcce1fa9c6eb89fb4f0e56421d277e/pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a", size = 9165596, upload-time = "2026-05-11T18:52:33.334Z" }, + { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" }, + { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" }, + { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" }, + { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" }, + { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" }, + { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" }, + { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" }, + { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" }, + { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" }, + { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" }, + { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" }, + { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" }, + { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" }, + { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" }, + { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" }, + { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" }, + { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" }, + { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" }, + { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" }, + { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" }, + { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" }, + { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" }, + { url = "https://files.pythonhosted.org/packages/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" }, + { url = "https://files.pythonhosted.org/packages/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" }, + { url = "https://files.pythonhosted.org/packages/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" }, + { url = "https://files.pythonhosted.org/packages/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" }, + { url = "https://files.pythonhosted.org/packages/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" }, + { url = "https://files.pythonhosted.org/packages/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" }, + { url = "https://files.pythonhosted.org/packages/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" }, + { url = "https://files.pythonhosted.org/packages/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" }, + { url = "https://files.pythonhosted.org/packages/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" }, + { url = "https://files.pythonhosted.org/packages/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "starlette" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" }, +] + +[[package]] +name = "texttable" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/dc/0aff23d6036a4d3bf4f1d8c8204c5c79c4437e25e0ae94ffe4bbb55ee3c2/texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638", size = 12831, upload-time = "2023-10-03T09:48:12.272Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917", size = 10768, upload-time = "2023-10-03T09:48:10.434Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.49.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/1f/fa18009dea8469069cca78a4e877a008ab78f08b064bfc9ab891579077ff/uvicorn-0.49.0.tar.gz", hash = "sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3", size = 91284, upload-time = "2026-06-03T22:01:30.448Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/fa/e1388bbcf24ef3274f45c0c1c7b501fd14971037c1b6ee23610553307497/uvicorn-0.49.0-py3-none-any.whl", hash = "sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f", size = 71376, upload-time = "2026-06-03T22:01:29.037Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" }, + { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" }, + { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" }, + { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" }, + { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, + { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/3d/8024c801df84d1587740d0359e7fdd80afeae3d159011f3d5376dd82f18e/watchfiles-1.2.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:704fd259e332e01f9b9c178f4bce9e49027e5587cc2600eeeaf8e76e1c846201", size = 400242, upload-time = "2026-05-18T04:31:19.014Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/f4dfd45323e949984a3a7f9dc31d1cbb049921e7d98253488dda72ccdaa9/watchfiles-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6543cf55d170003296d185c0af981f3e1311564907e1f4e08671fc7693a890a5", size = 394562, upload-time = "2026-05-18T04:30:08.46Z" }, + { url = "https://files.pythonhosted.org/packages/98/d8/19483ef075d601c409bce8bcbb5c0f81a10876fff870400568f08ce484a1/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d8c2394a065ca86f5d2910ff263ae67c127e1376ccc4f9fc35c71db879f80a", size = 456611, upload-time = "2026-05-18T04:30:45.723Z" }, + { url = "https://files.pythonhosted.org/packages/b1/6a/cc81fbe7ee42f2f22e661a6e12def7807e01b14b2f39e0ff83fd373fd307/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:772b80df316480d894a0e3165fdd19cf77f5d17f9a787f94029465ad0e3529d1", size = 461379, upload-time = "2026-05-18T04:31:29.292Z" }, + { url = "https://files.pythonhosted.org/packages/b1/57/7e669002082c0a0f4fb5113bb70125f7110124b846b0a11bc5ae8e90eac1/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d158cd89df6053823533e06fb1d73c549133bff5f0396170c0e53d9559340717", size = 493556, upload-time = "2026-05-18T04:30:05.44Z" }, + { url = "https://files.pythonhosted.org/packages/45/7d/f60a2b19807b21fe8281f3a8da4f59eef0d5f96825ac4680ba2d4f2ebf91/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d516b3283a758e087841aedb8031549fb41ced08f3db10aa6d2bf32dc042525b", size = 575255, upload-time = "2026-05-18T04:30:40.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/49/77f5b5e6efbcd57482f74948ebb1b97e5c0046d6b61475042d830c84b3ff/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53b2290c92e0506d102cd448fbc610d87079553f86caa39d67440856a8b8bba5", size = 467052, upload-time = "2026-05-18T04:31:17.942Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5a/73e2959af1b97fd5d556f9a8bdba017be23ceeef731869d5eaa0a753d5a3/watchfiles-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a711b51aec4370d0dcda5b6c09463206f133a5759341d7744b953a7b62e1100e", size = 456858, upload-time = "2026-05-18T04:30:30.182Z" }, + { url = "https://files.pythonhosted.org/packages/50/57/1bc8c27fad7e6c19bddee15d276dbb6ab72480ec01c127afff1673aee417/watchfiles-1.2.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:e2ca07fa7d89195ec0865d3d285666286740bfa83d83e5cee204043a31ecc165", size = 467579, upload-time = "2026-05-18T04:32:15.897Z" }, + { url = "https://files.pythonhosted.org/packages/09/6c/3c2e44edba3553c5e3c3b8c8a2a6dee6b9e12ae2cf4bd2378bebf9dc3038/watchfiles-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0618518f282c4ebff60f5e5b1247b6d91bb8b9f4476947563a1e74acc66f3c6", size = 633253, upload-time = "2026-05-18T04:31:37.123Z" }, + { url = "https://files.pythonhosted.org/packages/30/c2/d8c84a882ab39bbefcc4915ab3e91830b7a7e990c5570b0b69075aba3faf/watchfiles-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0d191c054d0715c3c95c99df9b8dbf6fd096d8c1e021e8f212e1bd8bc444ccb5", size = 660713, upload-time = "2026-05-18T04:31:24.62Z" }, + { url = "https://files.pythonhosted.org/packages/a9/07/f97736a5fc605364fe67b25e9fa4a6965dfd4840d50c406ada507e9d735f/watchfiles-1.2.0-cp311-cp311-win32.whl", hash = "sha256:9342472aff9b093c5acd4f6d8f70ae0937964ab56542502bcf5579782da69ae8", size = 277222, upload-time = "2026-05-18T04:31:21.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/99/2b04981977fc2608afd60360d928c6aecf6b950292ca221d98f4005f6694/watchfiles-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:dbd6c97045dad81227c8d040173da044c1de08de64a5ea8b555da4aee1d5fa22", size = 290274, upload-time = "2026-05-18T04:31:45.966Z" }, + { url = "https://files.pythonhosted.org/packages/3c/74/f7f58a7075ee9cf612b0cfcddb78b8cd8234f0742d6f0075cf0da2dde1c6/watchfiles-1.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:57a2d9fa4fb4c2ecae57b13dfff2c7ab53e21a2ba674fe9f05506680fcdcc0d7", size = 283460, upload-time = "2026-05-18T04:31:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/b8/2f/e42c992d2afda3108ea1c02acecc991b9f31d05c14adc2a7cee9ee211fc4/watchfiles-1.2.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bc13eb17538be00c874699dc0abe4ee2bc8d50bb1166a6b9e175ef3fd7eb8f26", size = 400115, upload-time = "2026-05-18T04:32:02.06Z" }, + { url = "https://files.pythonhosted.org/packages/5f/8f/6af2ea19065c91d8b0ea3516fdfc8c0d349f407e8e9fbf4e5a17360de8ad/watchfiles-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d95ddc1eb6914154253d239089900813f6a767e174b8e6a50e7fdacb7e4236c", size = 393659, upload-time = "2026-05-18T04:30:50.951Z" }, + { url = "https://files.pythonhosted.org/packages/13/01/b32a967c56fb3e3e5be3db52c3d3b87fa4513aa367d8ed1ad96d42952e5f/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f70d8b291ef6e88d19b1f297a6905ddb978888d9272b0d05e6f53309856bcfc", size = 453207, upload-time = "2026-05-18T04:31:04.231Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/97557a812180338cb1abd32e1cffcc4588f59b5f23e0cb006b2ba95ba64a/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56d8641cf834c2836922899105bd3ce3d0dfc69291d52edf0b4d0436829b34c0", size = 459273, upload-time = "2026-05-18T04:31:50.377Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a8/b4b08dcb7653b8087c6586f7ce649505900e866bbcfe40dc9587af02e686/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2581a94056e55d7d0a31a823ea92bf73749c489ca2285bfdc0fbe6b2bb49d50c", size = 489927, upload-time = "2026-05-18T04:31:42.485Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/3dceea03545d2e5ddfd839f0ddd5e1cecbf1697b5a428d5ba11cef6af95d/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41bc1199f7523b3f82843c88cbb979180c949caef0342cf90968f178e5d49b01", size = 570476, upload-time = "2026-05-18T04:31:03.071Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f2/d39a5450c3532092b91f81d274360e613c2371bc874a89c7a1a3c5e8d138/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7571e4464cb6e434958f867f7f730b8ab0b75e3f8e5eac0499168486ab3c33a8", size = 465650, upload-time = "2026-05-18T04:30:12.701Z" }, + { url = "https://files.pythonhosted.org/packages/22/24/ed72f68cbc1333ca9b9f2200aa048bb6658ae41709bc1caad4310f4bdffd/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5", size = 456398, upload-time = "2026-05-18T04:30:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/0d/64/982ef4a4e5bab5b6e5b6becc8cd5e732f6130a78b855f0abec6439a9a135/watchfiles-1.2.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d20029a60a71a052a24c4db7673bc4de39ab89adbaccbfb5d67987c5d73f424d", size = 465140, upload-time = "2026-05-18T04:31:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0c/95282abf4ed680b6096010bcfc30c5fa7a041fc5aa5a2ad17a2cc6c75bba/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2cb93af48550faf1cea04c303107c8b75833de7013e57ce27d3b8d21d8d0f58c", size = 630259, upload-time = "2026-05-18T04:31:25.676Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/607c1de1530c4bdcf2cf1d1ecc2505ddba5d96bd43ba9f2b0e79876f850f/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2995c176de7692b86a2e4c58d9ec718f753150a979cb4a754e2b4ffa38e70906", size = 659859, upload-time = "2026-05-18T04:30:24.333Z" }, + { url = "https://files.pythonhosted.org/packages/fa/08/d9e2e0f9e8e6791d33aefc694ad7eefa7f901f63caff84a81ded38692f9c/watchfiles-1.2.0-cp312-cp312-win32.whl", hash = "sha256:7a2cffd17d27d2ecbb310c2b1d8174f222a5495b1a721894afa88ec11e25b898", size = 275480, upload-time = "2026-05-18T04:30:31.307Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e6/9d42569c0102645cc8cea5d8c7d8a1e9d4ada2cb7f05f75e554b8aa2202a/watchfiles-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:f155b3a1b2a5fc89cdc70d47ee5d54e3b75e88efa34982028a35daef9ba00379", size = 288718, upload-time = "2026-05-18T04:32:10.745Z" }, + { url = "https://files.pythonhosted.org/packages/0a/26/88e0dc6ee3898169d7fa22bb6a69cabf2502d2ee25cb8c876d1262d204f8/watchfiles-1.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:8fa585ede612ee9f9e91b18bebf9ba11b9ae29a4e3a0d0cf6fca3e382133f0d5", size = 281026, upload-time = "2026-05-18T04:30:22.23Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730, upload-time = "2026-05-18T04:31:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842, upload-time = "2026-05-18T04:30:27.051Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989, upload-time = "2026-05-18T04:31:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978, upload-time = "2026-05-18T04:30:52.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248, upload-time = "2026-05-18T04:31:36.003Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847, upload-time = "2026-05-18T04:31:10.862Z" }, + { url = "https://files.pythonhosted.org/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974, upload-time = "2026-05-18T04:30:17.006Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782, upload-time = "2026-05-18T04:30:35.656Z" }, + { url = "https://files.pythonhosted.org/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182, upload-time = "2026-05-18T04:30:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841, upload-time = "2026-05-18T04:31:05.397Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028, upload-time = "2026-05-18T04:31:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183, upload-time = "2026-05-18T04:30:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059, upload-time = "2026-05-18T04:32:07.937Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186, upload-time = "2026-05-18T04:31:54.484Z" }, + { url = "https://files.pythonhosted.org/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031, upload-time = "2026-05-18T04:30:44.576Z" }, + { url = "https://files.pythonhosted.org/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205, upload-time = "2026-05-18T04:30:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892, upload-time = "2026-05-18T04:32:14.005Z" }, + { url = "https://files.pythonhosted.org/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867, upload-time = "2026-05-18T04:31:22.279Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217, upload-time = "2026-05-18T04:31:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458, upload-time = "2026-05-18T04:32:11.875Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707, upload-time = "2026-05-18T04:30:43.492Z" }, + { url = "https://files.pythonhosted.org/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663, upload-time = "2026-05-18T04:30:16.061Z" }, + { url = "https://files.pythonhosted.org/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537, upload-time = "2026-05-18T04:31:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194, upload-time = "2026-05-18T04:31:14.141Z" }, + { url = "https://files.pythonhosted.org/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194, upload-time = "2026-05-18T04:31:47.14Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/a9c7ea9a82a4ac65e7004c0a03920b5cdd2f9c3b678757d9cd425aa51d53/watchfiles-1.2.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4", size = 400205, upload-time = "2026-05-18T04:32:05.153Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/c9ab3534374a4a67450696905d6ef16a04405448b8dc52bd752ae50423d4/watchfiles-1.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281", size = 392508, upload-time = "2026-05-18T04:30:54.849Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/1ad30103535cf0cecd7b993e8d50edc5351b1820e38f2d22e3df58962feb/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d", size = 452448, upload-time = "2026-05-18T04:30:53.727Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/ceee2cdf2afbd715fa07758d39c9859513eae411b23196f7fd039e5feedd/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e", size = 459605, upload-time = "2026-05-18T04:30:23.312Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/421e30fd1cb3907a84ed92ab3f1983e37ba2dca015e9a894a048418417a2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242", size = 490757, upload-time = "2026-05-18T04:30:47.358Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/55ed1b97ed08be7bba6f9a541cac15f2a858e1d74d2b07b6da70a82aab00/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add", size = 568672, upload-time = "2026-05-18T04:30:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cf/d8ae8a80dd7bafab395ea7681c10237311bbf34d37704a8c744e7cf31fc7/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f", size = 464197, upload-time = "2026-05-18T04:30:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8a/3076c496ca8dafe0e8cd03fcebdfc47be4b1174b4e5b24ff6e396e6b3af2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7", size = 453181, upload-time = "2026-05-18T04:30:14.829Z" }, + { url = "https://files.pythonhosted.org/packages/e5/10/9745e17c98e7b8a86454df0a3c7b5686bd650383f1e9f26e4ebcbd6cc0c0/watchfiles-1.2.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e", size = 465109, upload-time = "2026-05-18T04:30:28.123Z" }, + { url = "https://files.pythonhosted.org/packages/8f/95/8ef4a95481d3e0cb52d62a06fa6e972e81424be2d9698b91a2fecca9904c/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06", size = 630653, upload-time = "2026-05-18T04:31:49.304Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e4/3b3bf36b0f829b50c6ebcb8d031583863c59f923d6a6af3d485e470d0fac/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba", size = 657838, upload-time = "2026-05-18T04:31:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/6cbbb50c1f3002ab568777d44aa21206dfb8807a840990c4037523b51812/watchfiles-1.2.0-cp314-cp314-win32.whl", hash = "sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7", size = 275108, upload-time = "2026-05-18T04:30:06.891Z" }, + { url = "https://files.pythonhosted.org/packages/92/45/190ce6db8dcb4536682cf75d3889ff1a27182a58cb519d343cb6d9ea63d8/watchfiles-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103", size = 288441, upload-time = "2026-05-18T04:32:12.901Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/3eae1c2313ab08378431d907c3f8095ecca00f3eda33111cf4f0f2591799/watchfiles-1.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3", size = 280684, upload-time = "2026-05-18T04:31:26.902Z" }, + { url = "https://files.pythonhosted.org/packages/b1/75/fb64e6c25d6b5ca636d03df34ffb1c6e9873303e76d27967e045f8df088f/watchfiles-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2", size = 398857, upload-time = "2026-05-18T04:32:17.108Z" }, + { url = "https://files.pythonhosted.org/packages/73/4e/9f7adf01754cbf81843722ccfec169d8f26c69778281a302855cecd2ee08/watchfiles-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28", size = 392413, upload-time = "2026-05-18T04:31:07.911Z" }, + { url = "https://files.pythonhosted.org/packages/47/c8/bec626bcc2d69f44b9acb24ce7d60ed7b16b73628eea747fcbd169d8edda/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831", size = 452409, upload-time = "2026-05-18T04:31:20.142Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/b6362068e81e7c556d155a34c35d40ac3ef42d747b06d7f6e5bf58e359c2/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33", size = 458827, upload-time = "2026-05-18T04:32:06.219Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/9a813fa42afb1e0b4625e75f0479826644d3ee8dc287e093799bc01f390c/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4", size = 490104, upload-time = "2026-05-18T04:31:56.034Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bf/27dfb6094ca4c9aad21298b5525b6c53cb36121ee454331d05161e58d130/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b", size = 571360, upload-time = "2026-05-18T04:31:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/fb/39/44a096d67270ea93df91d33877dbe91fbda3aa4f8ec2edf799d93eda8736/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666", size = 464644, upload-time = "2026-05-18T04:30:57.33Z" }, + { url = "https://files.pythonhosted.org/packages/0e/80/c7472203bad6268e3ef1ad260739704847898938ad7ea8b63a5131f46b50/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925", size = 454771, upload-time = "2026-05-18T04:30:48.736Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/3b10b268b4b7f0fc26e9debb5eef1998b515887840f444cd3ec80c688755/watchfiles-1.2.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b", size = 463494, upload-time = "2026-05-18T04:31:33.826Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3e/a4302545cd589262a0dc7d140e86f7688eba3f9c72776c27f7e23b8864c4/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30", size = 629383, upload-time = "2026-05-18T04:31:15.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/99/d5649df0a9a410d45b7c882304d0b790903ac9b6e8f2cfd12114e0c6b9f2/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5", size = 656093, upload-time = "2026-05-18T04:31:58.707Z" }, + { url = "https://files.pythonhosted.org/packages/92/b9/362702539275019a54dd2e94511b31a9b89c5f9e6a21966de7eb692549fc/watchfiles-1.2.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:37a6721cdf3f65dbb13aa9503510ccb4451603ac837e44d265d7992a597e1374", size = 400109, upload-time = "2026-05-18T04:31:16.879Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/71d5ba62db781e5587bded1d944c675374bc4aa37ff33d5018d98e8b6538/watchfiles-1.2.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2b37d10b5a63bd4d87e18472d80fa525bd670586fae62e5dd580452764879b65", size = 392167, upload-time = "2026-05-18T04:31:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/3c/01/c66dd95d0423fe30d31820e2d1d5bda773764131bbb6ac0cb1cf303ac328/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a105bc2283f67e8fbec74253ec2d94925de92ed72c0393f1206bf326b7b7b69", size = 452372, upload-time = "2026-05-18T04:31:00.836Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/2fe99557e72f85627c6a8eed50d889e8d101623e060a22ad75b875cb932d/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5327989a465505f05cfe06f04fa9d0c2fd5432bb243e10e6f012b1bdca3c8579", size = 459596, upload-time = "2026-05-18T04:31:34.96Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/d4acfa0023367428ed48351b3b9b267893037b6cadae55620c61c24bcfd4/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb47f183a8025b2aa18b546725c3657e542112ae9c0613a2af79b4fa8d04ad7", size = 490869, upload-time = "2026-05-18T04:31:59.923Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5f/3164cbdce06c9fb95c4f7b9e2f9760b5e2797af43a9ecc317ef42a23a278/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8520a4ab0e37f770afc34459c4f8f7019e153f9124dc101c15538365875d1ab2", size = 571641, upload-time = "2026-05-18T04:32:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/41/e6/85d3731c55e65cd7690f3f803d24c139588aaf863e4bf2148fe7a7fa1a19/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71cd71740ed2c15211ebb237ced4e39a1cdf6f80566e5fe95428da1626f4fde6", size = 464444, upload-time = "2026-05-18T04:30:34.298Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7d/562641012b8b09872742c3b8adf9629ec479fd78f8d68ae4a0c13da8add6/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f88af53d6ddaf72179ef613ddc905e6f4785f712b49b80b3bef9f3525e6194b4", size = 453593, upload-time = "2026-05-18T04:31:23.464Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/cb8ef3d6f929d14158fdaaad9925985b7310abc9384dcd4d82dd0016fb59/watchfiles-1.2.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:cee9d5efd929efdac5f7e58f72b3376f676b64050a91c5b99a7094c5b2317488", size = 465096, upload-time = "2026-05-18T04:31:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/25/91/80908e835e100527a9267147b08c0eee1fa6ab0ffec15edc04d1d44885f7/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:b718bf356bbc15e559bd8ef41782b573b8ae0e3f177ab244b440568d7ea02cfb", size = 630638, upload-time = "2026-05-18T04:30:49.89Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/95ab2f256bb4af3cb2eb23b9317bda984ee6e0f11733a5c004a6c95b06e3/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:922c0e019fe68b3ae392965a766b02a71ba1168c932cebc3733cd52c5fe5b377", size = 657684, upload-time = "2026-05-18T04:31:32.027Z" }, + { url = "https://files.pythonhosted.org/packages/23/f4/7513ef1e85fc4c6331b59479d6d72661fc391fbe543678052ac72c8b6c19/watchfiles-1.2.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4674d49eb94706dfe666c069fc0a1b646ffcf920473492e209f6d5f60d3f0cc2", size = 403050, upload-time = "2026-05-18T04:30:36.753Z" }, + { url = "https://files.pythonhosted.org/packages/27/0b/a54103cfd732bb703c7a749222011a0483ef3705948dae3b203158601119/watchfiles-1.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:094b9b70103d4e963499bdea001ee3c2697b144cd9ae6218a62c0f89ec9e31db", size = 396629, upload-time = "2026-05-18T04:32:03.268Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/73f31a3b893886206c3f54d73e8ad8dee58cdb2f69ad2622e0a8a9e07f4e/watchfiles-1.2.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0ef001f8c25ad0fa9529f914c1600647ecd0f542d11c19b7894768c67b6acb7", size = 457318, upload-time = "2026-05-18T04:31:01.932Z" }, + { url = "https://files.pythonhosted.org/packages/e9/f9/45d021e4a5cc7b9dd567f7cbb06d3b75f751a690063fb6cc7ec60f4e46b7/watchfiles-1.2.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a88fc94e647bc4eec523f1caa540258eb71d14278b9daf72fa1e2658a98df0f0", size = 457771, upload-time = "2026-05-18T04:30:56.331Z" }, +] + +[[package]] +name = "websockets" +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" }, + { url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" }, + { url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", size = 178246, upload-time = "2026-01-10T09:22:43.654Z" }, + { url = "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", size = 178684, upload-time = "2026-01-10T09:22:44.941Z" }, + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, + { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, + { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" }, + { url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" }, + { url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] From 23ce2e0d062ca955a910902d59d22a48891449ee Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 19:37:10 +0100 Subject: [PATCH 005/166] infra: docker-compose, prod Dockerfile, nginx, CI, pre-commit (Phase 1 complete) Dev: docker-compose with uv + node images, hot reload both sides, VITE_PROXY_TARGET for in-network proxying. Prod: multi-stage build, nginx serves Vite dist + proxies /api to uvicorn on one image (v2 r-base Dockerfile replaced; v2 preserved via v2-legacy tag). Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 46 ++++++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 24 +++++++++++++++++++++ Dockerfile | 39 ++++++++++++++++------------------ PLAN.md | 10 ++++----- docker-compose.yml | 28 ++++++++++++++++++++++++ frontend/vite.config.ts | 3 ++- nginx/nginx.conf | 22 +++++++++++++++++++ 7 files changed, 145 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .pre-commit-config.yaml create mode 100644 docker-compose.yml create mode 100644 nginx/nginx.conf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..17ab869 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: CI + +on: + push: + branches: [main, v3, "v3-*"] + pull_request: + +jobs: + backend: + runs-on: ubuntu-latest + defaults: + run: { working-directory: backend } + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + - run: uv sync + - run: uv run ruff check . + - run: uv run ruff format --check . + - run: uv run mypy app + - run: uv run pytest + + frontend: + runs-on: ubuntu-latest + defaults: + run: { working-directory: frontend } + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: { node-version: 22, cache: npm, cache-dependency-path: frontend/package-lock.json } + - run: npm ci + - run: npm run lint + - run: npx prettier --check src + - run: npx tsc -b --noEmit || npx tsc --noEmit + - run: npm test -- --run + + e2e: + runs-on: ubuntu-latest + needs: [backend, frontend] + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + - uses: actions/setup-node@v4 + with: { node-version: 22, cache: npm, cache-dependency-path: frontend/package-lock.json } + - run: cd backend && uv sync && (uv run uvicorn app.main:app --port 8000 &) + - run: cd frontend && npm ci && npx playwright install --with-deps chromium + - run: cd frontend && npm run test:e2e diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..44d95eb --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,24 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.0 + hooks: + - id: ruff-check + args: [--fix] + files: ^backend/ + - id: ruff-format + files: ^backend/ + + - repo: local + hooks: + - id: eslint + name: eslint + language: system + entry: bash -c 'cd frontend && npx eslint src' + files: ^frontend/src/.*\.ts$ + pass_filenames: false + - id: prettier + name: prettier + language: system + entry: bash -c 'cd frontend && npx prettier --check src' + files: ^frontend/src/ + pass_filenames: false diff --git a/Dockerfile b/Dockerfile index 80a142c..21d0b32 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,20 @@ -FROM r-base -#Description of the image -LABEL description="Arena3Dweb docker image" -LABEL maintainer="Fotis Baltoumas" -LABEL version="2.0" -#set the timezone -ENV TZ Europe/Athens -#copy the Rprofile.site file and containing the shiny port and CRAN repo url -COPY Rprofile.site /usr/lib/R/etc/ +# Production image: nginx serves static Vite build, proxies /api to uvicorn. +FROM node:22-slim AS frontend-build +WORKDIR /build +COPY frontend/package.json frontend/package-lock.json ./ +RUN npm ci +COPY frontend/ ./ +RUN npm run build -# install required R packages -RUN R -e 'install.packages(c("shiny","shinyjs","shinythemes","igraph","RColorBrewer","jsonlite","tidyr"))' +FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim +WORKDIR /app +RUN apt-get update && apt-get install -y --no-install-recommends nginx && rm -rf /var/lib/apt/lists/* +COPY backend/pyproject.toml backend/uv.lock ./ +RUN uv sync --frozen --no-group dev +COPY backend/app ./app +COPY nginx/nginx.conf /etc/nginx/nginx.conf +COPY --from=frontend-build /build/dist /usr/share/nginx/html -# copy the Arena3Dweb directory to the VM -COPY ./Arena3Dweb/ /root/Arena3DWeb/ - -RUN R -e 'install.packages(c("DT", "fst"))' - -#expose the shiny port -EXPOSE 3838 - -#set the default command to run, ie "R" -CMD ["R", "-e", "shiny::runApp('/root/Arena3DWeb/')"] +EXPOSE 8080 +# ponytail: sh -c instead of a supervisor; two processes, container dies if nginx dies +CMD ["sh", "-c", "uv run uvicorn app.main:app --host 127.0.0.1 --port 8000 & exec nginx -g 'daemon off;'"] diff --git a/PLAN.md b/PLAN.md index 2cc0d46..1f75a8e 100644 --- a/PLAN.md +++ b/PLAN.md @@ -19,11 +19,11 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. - [x] Set up `frontend/package.json` — Vite + TypeScript + ESLint + Prettier + Vitest - [x] Set up `frontend/vite.config.ts` — proxy `/api` to `localhost:8000` in dev - [x] Set up `frontend/tsconfig.json` -- [ ] Set up `docker-compose.yml` — hot-reload frontend + backend -- [ ] Set up `Dockerfile` — production single image (nginx + uvicorn) -- [ ] Set up `nginx/nginx.conf` — serve static build, proxy `/api` -- [ ] Set up `.github/workflows/ci.yml` — lint → typecheck → unit tests → E2E -- [ ] Set up `.pre-commit-config.yaml` — Ruff + ESLint/Prettier hooks +- [x] Set up `docker-compose.yml` — hot-reload frontend + backend +- [x] Set up `Dockerfile` — production single image (nginx + uvicorn) +- [x] Set up `nginx/nginx.conf` — serve static build, proxy `/api` +- [x] Set up `.github/workflows/ci.yml` — lint → typecheck → unit tests → E2E +- [x] Set up `.pre-commit-config.yaml` — Ruff + ESLint/Prettier hooks - [x] Create `MIGRATION.md` with full old-file → new-file checklist --- diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3ec205a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,28 @@ +services: + backend: + image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim + working_dir: /app + volumes: + - ./backend:/app + - backend-venv:/app/.venv + command: uv run uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload + ports: + - "8000:8000" + + frontend: + image: node:22-slim + working_dir: /app + volumes: + - ./frontend:/app + - frontend-modules:/app/node_modules + command: sh -c "npm install && npm run dev -- --host" + environment: + - VITE_PROXY_TARGET=http://backend:8000 + ports: + - "5173:5173" + depends_on: + - backend + +volumes: + backend-venv: + frontend-modules: diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index a57b0de..739c196 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -3,7 +3,8 @@ import { defineConfig } from 'vite' export default defineConfig({ server: { proxy: { - '/api': 'http://localhost:8000', + // VITE_PROXY_TARGET set by docker-compose (backend service hostname) + '/api': process.env.VITE_PROXY_TARGET ?? 'http://localhost:8000', }, }, }) diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..ba87202 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,22 @@ +worker_processes auto; +events { worker_connections 1024; } + +http { + include /etc/nginx/mime.types; + sendfile on; + client_max_body_size 20m; # network TSV / session JSON uploads + + server { + listen 8080; + + location /api/ { + proxy_pass http://127.0.0.1:8000; + proxy_set_header Host $host; + } + + location / { + root /usr/share/nginx/html; + try_files $uri /index.html; + } + } +} From ec450eb99f15af9485b35070aaec5757e5d4e286 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 19:41:11 +0100 Subject: [PATCH 006/166] backend: GET /api/config + typed frontend client (Phase 2 complete) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports config/server_variables.R + global_variables.R into app/config.py (RColorBrewer Set3/Set1 palettes inlined as hex). Endpoint serves the v2 handler_initializeGlobals payload plus node palette, floor defaults, topology metric and no-edge-layout lists. static_variables.R was mutable Shiny state, ui_variables.R a footer year — both obsolete. All four R config files deleted. Co-Authored-By: Claude Fable 5 --- MIGRATION.md | 8 ++--- PLAN.md | 10 +++--- backend/app/config.py | 67 ++++++++++++++++++++++++++++++++++- backend/app/main.py | 3 ++ backend/app/routers/config.py | 26 ++++++++++++++ backend/tests/test_config.py | 25 +++++++++++++ config/global_variables.R | 1 - config/server_variables.R | 31 ---------------- config/static_variables.R | 2 -- config/ui_variables.R | 1 - frontend/src/api/config.ts | 20 +++++++++++ frontend/src/main.ts | 9 +++++ frontend/tsconfig.json | 2 +- 13 files changed, 159 insertions(+), 46 deletions(-) create mode 100644 backend/app/routers/config.py create mode 100644 backend/tests/test_config.py delete mode 100644 config/global_variables.R delete mode 100644 config/server_variables.R delete mode 100644 config/static_variables.R delete mode 100644 config/ui_variables.R create mode 100644 frontend/src/api/config.ts diff --git a/MIGRATION.md b/MIGRATION.md index 68208cd..cd7bc4f 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -11,10 +11,10 @@ See `PLAN.md` for the ordered implementation steps. | Old file | New file | Done | |---|---|---| -| `config/server_variables.R` | `backend/app/config.py` | [ ] | -| `config/global_variables.R` | `backend/app/config.py` | [ ] | -| `config/static_variables.R` | `backend/app/config.py` | [ ] | -| `config/ui_variables.R` | *(absorbed into frontend config — see Removed)* | [ ] | +| `config/server_variables.R` | `backend/app/config.py` | [x] | +| `config/global_variables.R` | `backend/app/config.py` | [x] | +| `config/static_variables.R` | *(mutable Shiny state — no equivalent in stateless server)* | [x] | +| `config/ui_variables.R` | *(absorbed into frontend config — see Removed)* | [x] | | `functions/input.R` | `backend/app/services/parser.py` + `backend/app/routers/network.py` | [ ] | | `functions/init.R` | `backend/app/routers/config.py` + `backend/app/main.py` | [ ] | | `functions/general.R` | `backend/app/services/parser.py` (file read) + `backend/app/services/topology.py` (`mapper()`) — JS-bridge helpers die with Shiny | [ ] | diff --git a/PLAN.md b/PLAN.md index 1f75a8e..9ddb920 100644 --- a/PLAN.md +++ b/PLAN.md @@ -30,11 +30,11 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. ## Phase 2 — Backend: Config Endpoint -- [ ] Implement `backend/app/config.py` — port constants from `config/server_variables.R`; backend keeps what it validates against (`MAX_EDGES`, `MAX_LAYERS`, `MAX_CHANNELS`, mandatory/optional column lists, scale targets), purely visual constants (node palette, channel colors, floor defaults) also served via `/api/config` for the frontend -- [ ] Implement `GET /api/config` router -- [ ] Write pytest tests for `/api/config` -- [ ] Verify frontend can fetch and parse config response -- [ ] Delete `config/server_variables.R` (`global_variables.R`, `static_variables.R`, `ui_variables.R` are 1–2 lines each — fold their constants in here too and delete them) +- [x] Implement `backend/app/config.py` — port constants from `config/server_variables.R`; backend keeps what it validates against (`MAX_EDGES`, `MAX_LAYERS`, `MAX_CHANNELS`, mandatory/optional column lists, scale targets), purely visual constants (node palette, channel colors, floor defaults) also served via `/api/config` for the frontend +- [x] Implement `GET /api/config` router +- [x] Write pytest tests for `/api/config` +- [x] Verify frontend can fetch and parse config response (`src/api/config.ts` typed client + live smoke test) +- [x] Delete `config/server_variables.R` (`global_variables.R`, `static_variables.R`, `ui_variables.R` folded in / obsolete — deleted too) --- diff --git a/backend/app/config.py b/backend/app/config.py index d40a5be..9a4ac13 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -1 +1,66 @@ -# Application constants — ports config/server_variables.R (Phase 2) +"""Application constants — ports config/server_variables.R + global_variables.R. + +RColorBrewer palettes are inlined as hex (Set3 / Set1), matching v2 output exactly. +""" + +# Input validation +MANDATORY_NETWORK_COLUMNS = ["SourceNode", "SourceLayer", "TargetNode", "TargetLayer"] +OPTIONAL_NETWORK_COLUMNS = ["Channel", "Weight"] +MANDATORY_JSON_OBJECTS = ["layers", "nodes", "edges"] +OPTIONAL_JSON_OBJECTS = ["scene", "universalLabelColor", "direction", "edgeOpacityByWeight"] +MANDATORY_JSON_NODE_COLUMNS = ["name", "layer"] +MANDATORY_JSON_EDGE_COLUMNS = ["src", "trg"] +MAX_EDGES = 10_000 +MAX_CHANNELS = 9 +MAX_LAYERS = 18 + +# Topology scaling +DEFAULT_MAP_VALUE = 0.3 +TARGET_NODE_SCALE_MIN = 0.5 +TARGET_NODE_SCALE_MAX = 2.5 +TOPOLOGY_METRICS = ["Degree", "Clustering Coefficient", "Betweenness Centrality"] + +# Layouts +NO_EDGE_LAYOUTS = ["Circle", "Grid", "Random"] + +# Themes — RColorBrewer::brewer.pal(12, "Set3") +_SET3 = [ + "#8DD3C7", + "#FFFFB3", + "#BEBADA", + "#FB8072", + "#80B1D3", + "#FDB462", + "#B3DE69", + "#FCCDE5", + "#D9D9D9", + "#BC80BD", + "#CCEBC5", + "#FFED6F", +] +# brewer.pal(9, "Set1") +_SET1 = [ + "#E41A1C", + "#377EB8", + "#4DAF4A", + "#984EA3", + "#FF7F00", + "#FFFF33", + "#A65628", + "#F781BF", + "#999999", +] +NODE_COLORS = _SET3 + _SET3[:6] # 18 layers +EDGE_DEFAULT_COLOR = "#CFCFCF" +CHANNEL_COLORS_LIGHT = _SET3[:MAX_CHANNELS] +CHANNEL_COLORS_DARK = _SET1 + +# UI defaults +FLOOR_DEFAULT_COLOR = "#777777" +FLOOR_DEFAULT_WIDTH = 1000 + +# VR +VR_DOWNSCALE_FACTOR = 300 + +# Ephemeral storage for external-API sessions + VR output (SPEC §5) +TMP_PATH = "tmp/" diff --git a/backend/app/main.py b/backend/app/main.py index cad6aa8..726d570 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,6 +1,9 @@ from fastapi import FastAPI +from app.routers import config as config_router + app = FastAPI(title="Arena3Dweb API", version="3.0.0") +app.include_router(config_router.router) @app.get("/api/health") diff --git a/backend/app/routers/config.py b/backend/app/routers/config.py new file mode 100644 index 0000000..11fe128 --- /dev/null +++ b/backend/app/routers/config.py @@ -0,0 +1,26 @@ +"""GET /api/config — replaces handler_initializeGlobals push (functions/init.R).""" + +from typing import Any + +from fastapi import APIRouter + +from app import config + +router = APIRouter() + + +@router.get("/api/config") +async def get_config() -> dict[str, Any]: + return { + "max_edges": config.MAX_EDGES, + "max_channels": config.MAX_CHANNELS, + "max_layers": config.MAX_LAYERS, + "edge_default_color": config.EDGE_DEFAULT_COLOR, + "channel_colors_dark": config.CHANNEL_COLORS_DARK, + "channel_colors_light": config.CHANNEL_COLORS_LIGHT, + "node_colors": config.NODE_COLORS, + "floor_default_color": config.FLOOR_DEFAULT_COLOR, + "floor_default_width": config.FLOOR_DEFAULT_WIDTH, + "topology_metrics": config.TOPOLOGY_METRICS, + "no_edge_layouts": config.NO_EDGE_LAYOUTS, + } diff --git a/backend/tests/test_config.py b/backend/tests/test_config.py new file mode 100644 index 0000000..3091c3c --- /dev/null +++ b/backend/tests/test_config.py @@ -0,0 +1,25 @@ +from fastapi.testclient import TestClient + +from app.main import app + +client = TestClient(app) + + +def test_config_matches_v2_constants() -> None: + body = client.get("/api/config").json() + assert body["max_edges"] == 10_000 + assert body["max_layers"] == 18 + assert body["max_channels"] == 9 + assert body["edge_default_color"] == "#CFCFCF" + assert len(body["node_colors"]) == 18 + assert len(body["channel_colors_light"]) == 9 + assert len(body["channel_colors_dark"]) == 9 + assert body["channel_colors_dark"][0] == "#E41A1C" # brewer Set1[1] + assert body["node_colors"][0] == "#8DD3C7" # brewer Set3[1] + assert body["no_edge_layouts"] == ["Circle", "Grid", "Random"] + expected_metrics = ["Degree", "Clustering Coefficient", "Betweenness Centrality"] + assert body["topology_metrics"] == expected_metrics + + +def test_health() -> None: + assert client.get("/api/health").json() == {"status": "ok"} diff --git a/config/global_variables.R b/config/global_variables.R deleted file mode 100644 index fc9f95b..0000000 --- a/config/global_variables.R +++ /dev/null @@ -1 +0,0 @@ -TOPOLOGY_METRICS <- c("Degree", "Clustering Coefficient", "Betweenness Centrality") diff --git a/config/server_variables.R b/config/server_variables.R deleted file mode 100644 index 7afbd5e..0000000 --- a/config/server_variables.R +++ /dev/null @@ -1,31 +0,0 @@ -# Input #### -READ_LIBRARY <- "base" # options: base, fread; fread faster load but slower operations -MANDATORY_NETWORK_COLUMNS <- c("SourceNode", "SourceLayer", "TargetNode", "TargetLayer") -OPTIONAL_NETWORK_COLUMNS <- c("Channel", "Weight") -MANDATORY_JSON_OBJECTS <- c("layers", "nodes", "edges") -OPTIONAL_JSON_OBJECTS <- c("scene", "universalLabelColor", "direction", "edgeOpacityByWeight") -MANDATORY_JSON_NODE_COLUMNS <- c("name", "layer") -MANDATORY_JSON_EDGE_COLUMNS <- c("src", "trg") -MAX_EDGES <- 10000 -MAX_CHANNELS <- 9 -MAX_LAYERS <- 18 -DEFAULT_MAP_VALUE <- 0.3 -TARGET_NODE_SCALE_MIN <- 0.5 -TARGET_NODE_SCALE_MAX <- 2.5 -# API -POST_REQUEST_PATH <- 'tmp/' -API_URL <- "https://bib.fleming.gr/bib/api/arena3dweb/vr/" # "http://localhost:8080/api/arena3dweb/vr/" -# UI #### -HIDE_BUTTONS <- paste0("hideButton", c(1:8)) -FLOOR_DEFAULT_COLOR <- "#777777" -FLOOR_DEFAULT_WIDTH <- "1000" -# Themes #### -NODE_COLORS <- c(RColorBrewer::brewer.pal(12, 'Set3'), - RColorBrewer::brewer.pal(6, 'Set3')) # 18 Layers -EDGE_DEFAULT_COLOR = "#CFCFCF" -CHANNEL_COLORS_LIGHT <- RColorBrewer::brewer.pal(MAX_CHANNELS, 'Set3') -CHANNEL_COLORS_DARK <- RColorBrewer::brewer.pal(MAX_CHANNELS, 'Set1') -# VR #### -VR_DOWNSCALE_FACTOR <- 300 -# Layouts #### -NO_EDGE_LAYOUTS <- c("Circle", "Grid", "Random") diff --git a/config/static_variables.R b/config/static_variables.R deleted file mode 100644 index f4958af..0000000 --- a/config/static_variables.R +++ /dev/null @@ -1,2 +0,0 @@ -networkDF <- data.frame() -node_layerDF <- data.frame() diff --git a/config/ui_variables.R b/config/ui_variables.R deleted file mode 100644 index dfde144..0000000 --- a/config/ui_variables.R +++ /dev/null @@ -1 +0,0 @@ -YEAR <- substr(Sys.Date(), 1, 4) diff --git a/frontend/src/api/config.ts b/frontend/src/api/config.ts new file mode 100644 index 0000000..7f5e1e7 --- /dev/null +++ b/frontend/src/api/config.ts @@ -0,0 +1,20 @@ +// GET /api/config — replaces handler_initializeGlobals (v2 pushed these from R at startup) +export interface AppConfig { + max_edges: number + max_channels: number + max_layers: number + edge_default_color: string + channel_colors_dark: string[] + channel_colors_light: string[] + node_colors: string[] + floor_default_color: string + floor_default_width: number + topology_metrics: string[] + no_edge_layouts: string[] +} + +export async function fetchConfig(): Promise { + const res = await fetch('/api/config') + if (!res.ok) throw new Error(`GET /api/config failed: ${res.status}`) + return res.json() as Promise +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 814e93d..84fe971 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,2 +1,11 @@ // Entry point — Three.js canvas setup and app initialisation // Ports: www/js/on_page_load.js (Phase 12) +import { fetchConfig } from './api/config' + +fetchConfig() + .then((config) => { + console.log('[arena3d] config loaded', config) + }) + .catch((err: unknown) => { + console.error('[arena3d] config fetch failed', err) + }) diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 44704ff..e9d0fc9 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -10,5 +10,5 @@ "sourceMap": true, "lib": ["ES2022", "DOM", "DOM.Iterable"] }, - "include": ["src", "vite.config.ts", "eslint.config.js"] + "include": ["src"] } From 1efb6a35a8cdf7f02cae717e344fc600fc212fbc Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 19:46:43 +0100 Subject: [PATCH 007/166] backend: POST /api/network TSV parser + validation (Phase 3) Ports the parseUploadedNetwork() chain from functions/input.R: subset legit columns, trim, dedup (channel-aware), scale weights into 0.1..1 via mapper(), build Node_Layer keys. Validation mirrors isNetworkFormatValid() (missing mandatory columns, non-numeric weight, empty channel) as HTTP 400; MAX_LAYERS/MAX_EDGES enforced at the API boundary. 12 pytest cases incl. real aspirin_3channels.tsv fixture. input.R deletion deferred to Phase 7 (still holds JSON import, session export, attribute uploads). Added pandas-stubs; mypy target 3.12 for numpy stub syntax. Co-Authored-By: Claude Fable 5 --- PLAN.md | 12 +-- backend/app/main.py | 2 + backend/app/models/network.py | 29 ++++++++ backend/app/routers/network.py | 17 +++++ backend/app/services/parser.py | 129 +++++++++++++++++++++++++++++++++ backend/pyproject.toml | 5 +- backend/tests/test_network.py | 83 +++++++++++++++++++++ backend/uv.lock | 15 ++++ 8 files changed, 285 insertions(+), 7 deletions(-) create mode 100644 backend/app/models/network.py create mode 100644 backend/app/routers/network.py create mode 100644 backend/app/services/parser.py create mode 100644 backend/tests/test_network.py diff --git a/PLAN.md b/PLAN.md index 9ddb920..a985203 100644 --- a/PLAN.md +++ b/PLAN.md @@ -40,12 +40,12 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. ## Phase 3 — Backend: Network Parsing -- [ ] Implement Pydantic models — `NodeModel`, `EdgeModel`, `NetworkModel` (`models/network.py`) -- [ ] Implement `services/parser.py` — port TSV parsing and validation from `functions/input.R` -- [ ] Implement `POST /api/network` router -- [ ] Write pytest tests using `www/data/` TSV files as fixtures -- [ ] Verify validation errors match current behaviour (missing columns, non-numeric weights, empty channels) -- [ ] Delete `functions/input.R` +- [x] Implement Pydantic models — `NodeModel`, `EdgeModel`, `NetworkModel` (`models/network.py`) +- [x] Implement `services/parser.py` — port TSV parsing and validation from `functions/input.R` (`parseUploadedNetwork` chain + `mapper`) +- [x] Implement `POST /api/network` router +- [x] Write pytest tests using `www/data/` TSV files as fixtures (12 tests, real `aspirin_3channels.tsv`) +- [x] Verify validation errors match current behaviour (missing columns, non-numeric weights, empty channels) +- [ ] Delete `functions/input.R` — **deferred to Phase 7**: file also holds JSON import, session export, node/edge attribute uploads, example load. Only the TSV-upload portion is ported now. --- diff --git a/backend/app/main.py b/backend/app/main.py index 726d570..021c59a 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,9 +1,11 @@ from fastapi import FastAPI from app.routers import config as config_router +from app.routers import network as network_router app = FastAPI(title="Arena3Dweb API", version="3.0.0") app.include_router(config_router.router) +app.include_router(network_router.router) @app.get("/api/health") diff --git a/backend/app/models/network.py b/backend/app/models/network.py new file mode 100644 index 0000000..b185528 --- /dev/null +++ b/backend/app/models/network.py @@ -0,0 +1,29 @@ +"""Pydantic schemas for a parsed network — response of POST /api/network.""" + +from pydantic import BaseModel + + +class NodeModel(BaseModel): + id: str # "Node_Layer" + label: str + layer: str + + +class EdgeModel(BaseModel): + src: str # "SourceNode_Layer" + trg: str # "TargetNode_Layer" + source_node: str + source_layer: str + target_node: str + target_layer: str + weight: float + scaled_weight: float + channel: str | None = None + + +class NetworkModel(BaseModel): + nodes: list[NodeModel] + edges: list[EdgeModel] + layers: list[str] + channels: list[str] + warnings: list[str] = [] diff --git a/backend/app/routers/network.py b/backend/app/routers/network.py new file mode 100644 index 0000000..6966d6e --- /dev/null +++ b/backend/app/routers/network.py @@ -0,0 +1,17 @@ +"""POST /api/network — TSV upload. Replaces handleUploadNetwork() (functions/input.R).""" + +from fastapi import APIRouter, HTTPException, UploadFile + +from app.models.network import NetworkModel +from app.services.parser import NetworkValidationError, parse_network_tsv + +router = APIRouter() + + +@router.post("/api/network", response_model=NetworkModel) +async def upload_network(file: UploadFile) -> NetworkModel: + raw = (await file.read()).decode("utf-8", errors="replace") + try: + return parse_network_tsv(raw) + except NetworkValidationError as e: + raise HTTPException(status_code=400, detail=str(e)) from e diff --git a/backend/app/services/parser.py b/backend/app/services/parser.py new file mode 100644 index 0000000..8393cb6 --- /dev/null +++ b/backend/app/services/parser.py @@ -0,0 +1,129 @@ +"""TSV network parsing + validation — ports functions/input.R (upload path). + +Chain mirrors parseUploadedNetwork(): subset legit columns, trim, dedup, +scale weights, append Node_Layer keys. Validation mirrors isNetworkFormatValid(). +Blocking failures raise NetworkValidationError → HTTP 400 in the router. +""" + +from io import StringIO + +import pandas as pd + +from app import config +from app.models.network import EdgeModel, NetworkModel, NodeModel + + +class NetworkValidationError(ValueError): + """A blocking validation failure — bad file format.""" + + +def _mapper(values: pd.Series, out_min: float, out_max: float) -> pd.Series: + """Linear-rescale into [out_min, out_max]; constant input → DEFAULT_MAP_VALUE. + + Ports functions/general.R mapper() (used with 0.1..1 for scaled weights). + """ + in_min, in_max = values.min(), values.max() + if in_max - in_min == 0: + return pd.Series([config.DEFAULT_MAP_VALUE] * len(values), index=values.index) + scaled = (values - in_min) * (out_max - out_min) / (in_max - in_min) + out_min + return pd.Series(scaled, index=values.index) + + +def _validate(df: pd.DataFrame) -> None: + if not set(config.MANDATORY_NETWORK_COLUMNS).issubset(df.columns): + raise NetworkValidationError( + "Your network file must contain at least these four columns: " + "SourceNode, SourceLayer, TargetNode, TargetLayer" + ) + if "Weight" in df.columns and not pd.to_numeric(df["Weight"], errors="coerce").notna().all(): + raise NetworkValidationError("Make sure all input weights are numeric values.") + if "Channel" in df.columns and (df["Channel"].fillna("").astype(str).str.strip() == "").any(): + raise NetworkValidationError( + "At least one edge has no channel name. " + "Please reupload the network file with all edge channels." + ) + + +def parse_network_tsv(text: str) -> NetworkModel: + """Parse a raw TSV string into a validated NetworkModel.""" + df = pd.read_csv(StringIO(text), sep="\t", dtype=str) + _validate(df) + + # subset legit columns (mandatory + optional Channel/Weight, in fixed order) + legit = [ + c + for c in config.MANDATORY_NETWORK_COLUMNS + config.OPTIONAL_NETWORK_COLUMNS + if c in df.columns + ] + df = df[legit].copy() + + # trim whitespace on every column + for col in df.columns: + df[col] = df[col].astype(str).str.strip() + + # dedup — include Channel in the key when present + subset = list(config.MANDATORY_NETWORK_COLUMNS) + if "Channel" in df.columns: + subset.append("Channel") + df = df.drop_duplicates(subset=subset).reset_index(drop=True) + + # weights: numeric or default 1; scaled into 0.1..1 + weight = ( + pd.to_numeric(df["Weight"], errors="coerce") + if "Weight" in df.columns + else pd.Series([1.0] * len(df)) + ) + df["Weight"] = weight + df["ScaledWeight"] = _mapper(weight, 0.1, 1.0) + + df["SourceNode_Layer"] = df["SourceNode"] + "_" + df["SourceLayer"] + df["TargetNode_Layer"] = df["TargetNode"] + "_" + df["TargetLayer"] + + layers = pd.unique(pd.concat([df["SourceLayer"], df["TargetLayer"]]).dropna()).tolist() + if len(layers) > config.MAX_LAYERS: + raise NetworkValidationError( + f"The network must contain no more than {config.MAX_LAYERS} layers." + ) # noqa: E501 + if len(df) > config.MAX_EDGES: + raise NetworkValidationError( + f"The network must contain no more than {config.MAX_EDGES} edges." + ) # noqa: E501 + + return NetworkModel( + nodes=_build_nodes(df), + edges=_build_edges(df), + layers=layers, + channels=sorted(df["Channel"].unique().tolist()) if "Channel" in df.columns else [], + ) + + +def _build_nodes(df: pd.DataFrame) -> list[NodeModel]: + src = df[["SourceNode_Layer", "SourceNode", "SourceLayer"]].rename( + columns={"SourceNode_Layer": "id", "SourceNode": "label", "SourceLayer": "layer"} + ) + trg = df[["TargetNode_Layer", "TargetNode", "TargetLayer"]].rename( + columns={"TargetNode_Layer": "id", "TargetNode": "label", "TargetLayer": "layer"} + ) + nodes = pd.concat([src, trg]).drop_duplicates(subset="id") + return [ + NodeModel(id=str(r["id"]), label=str(r["label"]), layer=str(r["layer"])) + for r in nodes.to_dict("records") + ] + + +def _build_edges(df: pd.DataFrame) -> list[EdgeModel]: + has_channel = "Channel" in df.columns + return [ + EdgeModel( + src=r["SourceNode_Layer"], + trg=r["TargetNode_Layer"], + source_node=r["SourceNode"], + source_layer=r["SourceLayer"], + target_node=r["TargetNode"], + target_layer=r["TargetLayer"], + weight=r["Weight"], + scaled_weight=r["ScaledWeight"], + channel=r["Channel"] if has_channel else None, + ) + for r in df.to_dict("records") + ] diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 58ccbc0..3317165 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -18,17 +18,20 @@ dev = [ "pytest-asyncio>=0.24.0", "mypy>=1.11.0", "ruff>=0.6.0", + "pandas-stubs>=3.0.3.260530", ] [tool.ruff] line-length = 100 target-version = "py311" +# ruff config lives in backend/; legacy R-side scripts live above it (../www) — never lint those +extend-exclude = ["../www", "../functions", "../views", "../config"] [tool.ruff.lint] select = ["E", "F", "I", "UP", "B"] [tool.mypy] -python_version = "3.11" +python_version = "3.12" # numpy/pandas stubs require 3.12+ type syntax strict = true [tool.pytest.ini_options] diff --git a/backend/tests/test_network.py b/backend/tests/test_network.py new file mode 100644 index 0000000..215863e --- /dev/null +++ b/backend/tests/test_network.py @@ -0,0 +1,83 @@ +from pathlib import Path + +import pytest +from fastapi.testclient import TestClient + +from app.main import app +from app.services.parser import NetworkValidationError, parse_network_tsv + +client = TestClient(app) +DATA = Path(__file__).resolve().parents[2] / "www" / "data" +HDR = "SourceNode\tSourceLayer\tTargetNode\tTargetLayer" + + +def _post(text: str): + return client.post( + "/api/network", files={"file": ("net.tsv", text, "text/tab-separated-values")} + ) + + +def test_parses_real_multichannel_fixture() -> None: + fixture = DATA / "aspirin_3channels.tsv" + if not fixture.exists(): + pytest.skip("fixture missing") + body = _post(fixture.read_text()).json() + assert len(body["nodes"]) > 0 + assert len(body["edges"]) > 0 + assert "Drug" in body["layers"] + assert body["channels"] # multichannel file → non-empty + + +def test_scaled_weight_in_range() -> None: + tsv = f"{HDR}\tWeight\nA\tL1\tB\tL2\t5\nC\tL1\tD\tL2\t10\n" + edges = _post(tsv).json()["edges"] + scaled = [e["scaled_weight"] for e in edges] + assert min(scaled) == pytest.approx(0.1) + assert max(scaled) == pytest.approx(1.0) + + +def test_constant_weight_uses_default_map_value() -> None: + tsv = f"{HDR}\tWeight\nA\tL1\tB\tL2\t3\nC\tL1\tD\tL2\t3\n" + edges = _post(tsv).json()["edges"] + assert all(e["scaled_weight"] == pytest.approx(0.3) for e in edges) + + +def test_missing_weight_column_defaults_to_one() -> None: + tsv = f"{HDR}\nA\tL1\tB\tL2\n" + edges = _post(tsv).json()["edges"] + assert edges[0]["weight"] == 1.0 + + +def test_missing_mandatory_column_400() -> None: + tsv = "SourceNode\tSourceLayer\tTargetNode\nA\tL1\tB\n" + resp = _post(tsv) + assert resp.status_code == 400 + assert "four columns" in resp.json()["detail"] + + +def test_non_numeric_weight_400() -> None: + tsv = f"{HDR}\tWeight\nA\tL1\tB\tL2\theavy\n" + assert _post(tsv).status_code == 400 + + +def test_empty_channel_400() -> None: + tsv = f"{HDR}\tChannel\nA\tL1\tB\tL2\t\n" + assert _post(tsv).status_code == 400 + + +def test_duplicate_rows_collapsed() -> None: + tsv = f"{HDR}\nA\tL1\tB\tL2\nA\tL1\tB\tL2\n" + assert len(_post(tsv).json()["edges"]) == 1 + + +def test_whitespace_trimmed() -> None: + tsv = f"{HDR}\n A \tL1\tB\tL2\n" + node_ids = [n["id"] for n in _post(tsv).json()["nodes"]] + assert "A_L1" in node_ids + + +def test_too_many_layers_rejected() -> None: + rows = "\n".join(f"A\tL{i}\tB\tL{i + 1}" for i in range(0, 40, 2)) + tsv = f"{HDR}\n" + rows + "\n" + with pytest.raises(NetworkValidationError): + parse_network_tsv(tsv) diff --git a/backend/uv.lock b/backend/uv.lock index c0bdbff..720ffcf 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -64,6 +64,7 @@ dependencies = [ dev = [ { name = "httpx" }, { name = "mypy" }, + { name = "pandas-stubs" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "ruff" }, @@ -83,6 +84,7 @@ requires-dist = [ dev = [ { name = "httpx", specifier = ">=0.27.0" }, { name = "mypy", specifier = ">=1.11.0" }, + { name = "pandas-stubs", specifier = ">=3.0.3.260530" }, { name = "pytest", specifier = ">=8.3.0" }, { name = "pytest-asyncio", specifier = ">=0.24.0" }, { name = "ruff", specifier = ">=0.6.0" }, @@ -649,6 +651,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, ] +[[package]] +name = "pandas-stubs" +version = "3.0.3.260530" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/aa/c41a8a0ff86fd85dbb3ec0c1f3fa488ca64a8b5f82654ae1b07d84acefe5/pandas_stubs-3.0.3.260530.tar.gz", hash = "sha256:d1efe47b2e5a312c047d7feabec5cb7a55365747983420077e9fcbe9ab74f714", size = 113183, upload-time = "2026-05-30T17:47:40.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/e0/99ec5b02203c4e9ce878bc63d8caa06ac1f891e4d63bded9a5ced70fcb4f/pandas_stubs-3.0.3.260530-py3-none-any.whl", hash = "sha256:a6277eb1c8cebf48d9b2413fcd2e9a6b4ff479c934a223c29eacbc3058c4cb55", size = 173780, upload-time = "2026-05-30T17:47:39.13Z" }, +] + [[package]] name = "pathspec" version = "1.1.1" From 809c893dcafdb50a9d248c0e0b9a5d78337621e7 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 23:32:50 +0100 Subject: [PATCH 008/166] backend: POST /api/layout with 11 igraph layouts (Phase 4) Ports functions/igraph/layout.R + general.R graph construction: - services/graph.py: channel filter, perLayer/allLayers/nodesPerLayers scopes, simplify (sum parallel weights, keep loops) - services/layouts.py: registry of all 11 v2 UI layouts as Graph.layout_* calls, per-request RNG seed (v2 set.seed(123)), returns 2D [y,z] - no-edge layouts (Circle/Grid/Random) place isolated layer nodes as extra vertices (simpler equivalent of v2 filterPseudoNetwork) 24 pytest cases. layout.R deleted; general.R kept as spec through Phase 6. Co-Authored-By: Claude Fable 5 --- PLAN.md | 14 +- backend/app/main.py | 2 + backend/app/models/layout.py | 25 ++++ backend/app/routers/layout.py | 16 ++ backend/app/services/graph.py | 70 +++++++++ backend/app/services/layouts.py | 91 ++++++++++++ backend/pyproject.toml | 4 + backend/tests/test_layout.py | 160 ++++++++++++++++++++ functions/igraph/layout.R | 250 -------------------------------- 9 files changed, 375 insertions(+), 257 deletions(-) create mode 100644 backend/app/models/layout.py create mode 100644 backend/app/routers/layout.py create mode 100644 backend/app/services/graph.py create mode 100644 backend/app/services/layouts.py create mode 100644 backend/tests/test_layout.py delete mode 100644 functions/igraph/layout.R diff --git a/PLAN.md b/PLAN.md index a985203..23dc86f 100644 --- a/PLAN.md +++ b/PLAN.md @@ -52,13 +52,13 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. ## Phase 4 — Backend: Layout Algorithms - [x] Swap `networkx` + `scipy` + `python-louvain` for `python-igraph`; backend now managed by `uv` (`pyproject.toml` + `uv.lock`, `requirements.txt` deleted) -- [ ] Implement `services/graph.py` — `ig.Graph` construction (port `functions/igraph/general.R`: channel filter, perLayer/allLayers/nodesPerLayers subgraph scopes, `simplify()` multi-edge/loop rules) -- [ ] Implement `services/layouts.py` — registry dict mapping all 11 UI layout names to `Graph.layout_*` calls (port `getLayoutFunction()`) -- [ ] Port pseudo-network for no-edge layouts (`NO_EDGE_LAYOUTS` = Circle, Grid, Random) — `filterPseudoNetwork()` chains isolated nodes with tiny weights so whole layer participates -- [ ] Seed RNG per request (v2 uses `set.seed(123)`) — layouts must be reproducible -- [ ] Implement `POST /api/layout` router — scope + selected layers/nodes/channels params, returns 2D in-layer `[y, z]` coords (SPEC §5 flow) -- [ ] Write pytest tests for each layout with fixture graphs -- [ ] Delete `functions/igraph/layout.R`, `functions/igraph/general.R` +- [x] Implement `services/graph.py` — `ig.Graph` construction (port `functions/igraph/general.R`: channel filter, perLayer/allLayers/nodesPerLayers subgraph scopes, `simplify()` multi-edge/loop rules) +- [x] Implement `services/layouts.py` — registry dict mapping all 11 UI layout names to `Graph.layout_*` calls (port `getLayoutFunction()`) +- [x] Port pseudo-network for no-edge layouts (`NO_EDGE_LAYOUTS` = Circle, Grid, Random) — simplified: add all layer nodes as isolated vertices so `layout_circle/grid/random` place them (v2 chained them with tiny weights; same visible result) +- [x] Seed RNG per request (v2 uses `set.seed(123)`) — layouts must be reproducible +- [x] Implement `POST /api/layout` router — scope + selected layers/nodes/channels params, returns 2D in-layer `[y, z]` coords (SPEC §5 flow) +- [x] Write pytest tests for each layout with fixture graphs (24 tests: all 11 algos run, 3 scopes, channel filter, seed determinism) +- [x] Delete `functions/igraph/layout.R` (`general.R` ported into `graph.py` but kept as living spec until `cluster.R`/`topology.R` land in Phases 5–6) --- diff --git a/backend/app/main.py b/backend/app/main.py index 021c59a..85d6e98 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,11 +1,13 @@ from fastapi import FastAPI from app.routers import config as config_router +from app.routers import layout as layout_router from app.routers import network as network_router app = FastAPI(title="Arena3Dweb API", version="3.0.0") app.include_router(config_router.router) app.include_router(network_router.router) +app.include_router(layout_router.router) @app.get("/api/health") diff --git a/backend/app/models/layout.py b/backend/app/models/layout.py new file mode 100644 index 0000000..809adb6 --- /dev/null +++ b/backend/app/models/layout.py @@ -0,0 +1,25 @@ +"""Request/response schemas for POST /api/layout.""" + +from typing import Literal + +from pydantic import BaseModel + +from app.models.network import EdgeModel, NodeModel + +Scope = Literal["perLayer", "allLayers", "nodesPerLayers"] + + +class LayoutRequest(BaseModel): + nodes: list[NodeModel] + edges: list[EdgeModel] + algorithm: str + scope: Scope = "perLayer" + selected_layers: list[str] + selected_nodes: list[str] | None = None # Node_Layer ids, for nodesPerLayers + selected_channels: list[str] | None = None + seed: int = 123 # v2 uses set.seed(123) + + +class LayoutResponse(BaseModel): + # Node_Layer id -> [y, z] within the layer plane (x is fixed by the layer) + positions: dict[str, tuple[float, float]] diff --git a/backend/app/routers/layout.py b/backend/app/routers/layout.py new file mode 100644 index 0000000..94e56eb --- /dev/null +++ b/backend/app/routers/layout.py @@ -0,0 +1,16 @@ +"""POST /api/layout — replaces functions/igraph/layout.R.""" + +from fastapi import APIRouter, HTTPException + +from app.models.layout import LayoutRequest, LayoutResponse +from app.services.layouts import LayoutError, compute_layout + +router = APIRouter() + + +@router.post("/api/layout", response_model=LayoutResponse) +async def run_layout(req: LayoutRequest) -> LayoutResponse: + try: + return LayoutResponse(positions=compute_layout(req)) + except LayoutError as e: + raise HTTPException(status_code=400, detail=str(e)) from e diff --git a/backend/app/services/graph.py b/backend/app/services/graph.py new file mode 100644 index 0000000..e00d8b1 --- /dev/null +++ b/backend/app/services/graph.py @@ -0,0 +1,70 @@ +"""ig.Graph construction from a parsed network — ports functions/igraph/general.R. + +Handles channel filtering, the three subgraph scopes (perLayer / allLayers / +nodesPerLayers) and the v2 simplify rules (sum parallel-edge weights, keep loops). +""" + +import igraph as ig + +from app.models.network import EdgeModel + + +def filter_edges( + edges: list[EdgeModel], + selected_channels: list[str] | None, +) -> list[EdgeModel]: + """Drop edges whose channel is not selected (ports filterSelectedChannels). + + Only applies when the network has channels; otherwise all edges pass. + """ + has_channels = any(e.channel is not None for e in edges) + if not has_channels or selected_channels is None: + return list(edges) + chosen = set(selected_channels) + return [e for e in edges if e.channel in chosen] + + +def intra_layer_edges(edges: list[EdgeModel], layer: str) -> list[EdgeModel]: + """Edges with both endpoints in `layer` (ports filterPerLayer).""" + return [e for e in edges if e.source_layer == layer and e.target_layer == layer] + + +def within_layers_edges(edges: list[EdgeModel], layers: set[str]) -> list[EdgeModel]: + """Edges with both endpoints among `layers` (ports filterAllSelectedLayers).""" + return [e for e in edges if e.source_layer in layers and e.target_layer in layers] + + +def build_graph( + edges: list[EdgeModel], + *, + remove_multiple: bool, + extra_vertices: list[str] | None = None, +) -> ig.Graph: + """Build a simplified weighted undirected graph from edge rows. + + Ports createGraph(): edge weight = ScaledWeight, simplify with + combine_edges="sum", loops kept. `extra_vertices` adds isolated nodes + (used by no-edge layouts so every layer node is placed). + """ + names: list[str] = [] + seen: set[str] = set() + for e in edges: + for name in (e.src, e.trg): + if name not in seen: + seen.add(name) + names.append(name) + for name in extra_vertices or []: + if name not in seen: + seen.add(name) + names.append(name) + + index = {name: i for i, name in enumerate(names)} + edge_list = [(index[e.src], index[e.trg]) for e in edges] + weights = [e.scaled_weight for e in edges] + + graph = ig.Graph(n=len(names), edges=edge_list) + graph.vs["name"] = names + graph.es["weight"] = weights + # remove_loops=False matches v2 (remove.loops = F); sum aggregates parallel edges + graph.simplify(multiple=remove_multiple, loops=False, combine_edges={"weight": "sum"}) + return graph diff --git a/backend/app/services/layouts.py b/backend/app/services/layouts.py new file mode 100644 index 0000000..9695718 --- /dev/null +++ b/backend/app/services/layouts.py @@ -0,0 +1,91 @@ +"""Layout registry + orchestration — ports functions/igraph/layout.R. + +Maps the 11 v2 UI layout names to python-igraph Graph.layout_* calls, runs +the chosen scope, and returns 2D [y, z] coordinates keyed by Node_Layer id. +No weights are passed to layout functions — matches v2 (calculateLayout calls +the layout with defaults even though the graph carries weights). +""" + +import random +from collections.abc import Callable + +import igraph as ig + +from app import config +from app.models.layout import LayoutRequest +from app.models.network import EdgeModel +from app.services import graph as g + +LayoutFn = Callable[[ig.Graph], ig.Layout] + +LAYOUTS: dict[str, LayoutFn] = { + "Fruchterman-Reingold": lambda gr: gr.layout_fruchterman_reingold(), + "Reingold-Tilford": lambda gr: gr.layout_reingold_tilford(), + "Circle": lambda gr: gr.layout_circle(), + "Grid": lambda gr: gr.layout_grid(), + "Random": lambda gr: gr.layout_random(), + "DrL": lambda gr: gr.layout_drl(), + "Graphopt": lambda gr: gr.layout_graphopt(), + "Kamada-Kawai": lambda gr: gr.layout_kamada_kawai(), + "Large Graph Layout": lambda gr: gr.layout_lgl(), + "Multidimensional Scaling": lambda gr: gr.layout_mds(), + "Sugiyama": lambda gr: gr.layout_sugiyama(), +} + + +class LayoutError(ValueError): + """Unknown algorithm or a scope with no computable subgraph.""" + + +def compute_layout(req: LayoutRequest) -> dict[str, tuple[float, float]]: + if req.algorithm not in LAYOUTS: + raise LayoutError(f"Unknown layout algorithm: {req.algorithm}") + + random.seed(req.seed) # v2 set.seed(123) + edges = g.filter_edges(req.edges, req.selected_channels) + selected = set(req.selected_layers) + no_edge = req.algorithm in config.NO_EDGE_LAYOUTS + + # nodesPerLayers narrows to selected nodes first (ports runLocalLayout) + if req.scope == "nodesPerLayers": + chosen_nodes = set(req.selected_nodes or []) + edges = [e for e in edges if e.src in chosen_nodes and e.trg in chosen_nodes] + + positions: dict[str, tuple[float, float]] = {} + if req.scope == "allLayers": + layer_edges = g.within_layers_edges(edges, selected) + extra = _layer_nodes(req, selected) if no_edge else None + positions.update(_layout_subgraph(layer_edges, req.algorithm, no_edge, extra)) + else: # perLayer or nodesPerLayers — one subgraph per selected layer + for layer in req.selected_layers: + layer_edges = g.intra_layer_edges(edges, layer) + extra = _layer_nodes(req, {layer}) if no_edge else None + positions.update(_layout_subgraph(layer_edges, req.algorithm, no_edge, extra)) + return positions + + +def _layer_nodes(req: LayoutRequest, layers: set[str]) -> list[str]: + """All node ids belonging to `layers` (so no-edge layouts place isolated nodes).""" + ids = [n.id for n in req.nodes if n.layer in layers] + if req.scope == "nodesPerLayers" and req.selected_nodes is not None: + chosen = set(req.selected_nodes) + ids = [i for i in ids if i in chosen] + return ids + + +def _layout_subgraph( + edges: list[EdgeModel], + algorithm: str, + no_edge: bool, + extra_vertices: list[str] | None, +) -> dict[str, tuple[float, float]]: + if not edges and not extra_vertices: + return {} # nothing to place (v2: "cannot form a graph") + # no channels here → remove parallel edges (edge-based scopes rebuild per layer) + graph = g.build_graph(edges, remove_multiple=True, extra_vertices=extra_vertices) + if graph.vcount() == 0: + return {} + coords = LAYOUTS[algorithm](graph) + names = graph.vs["name"] + # Sugiyama appends dummy vertices for edge bends — keep only real vertices + return {names[i]: (float(coords[i][0]), float(coords[i][1])) for i in range(graph.vcount())} diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 3317165..71f48b8 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -34,5 +34,9 @@ select = ["E", "F", "I", "UP", "B"] python_version = "3.12" # numpy/pandas stubs require 3.12+ type syntax strict = true +[[tool.mypy.overrides]] +module = "igraph.*" +ignore_missing_imports = true + [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/backend/tests/test_layout.py b/backend/tests/test_layout.py new file mode 100644 index 0000000..ca3000c --- /dev/null +++ b/backend/tests/test_layout.py @@ -0,0 +1,160 @@ +import pytest +from fastapi.testclient import TestClient + +from app.main import app +from app.models.layout import LayoutRequest +from app.services.layouts import LAYOUTS, compute_layout + +client = TestClient(app) + + +def _net(): + # two layers, a small clique each, one inter-layer edge + nodes = [ + {"id": "A_L1", "label": "A", "layer": "L1"}, + {"id": "B_L1", "label": "B", "layer": "L1"}, + {"id": "C_L1", "label": "C", "layer": "L1"}, + {"id": "D_L2", "label": "D", "layer": "L2"}, + {"id": "E_L2", "label": "E", "layer": "L2"}, + ] + + def edge(src, trg, sl, tl): + s, t = src.split("_")[0], trg.split("_")[0] + return { + "src": src, + "trg": trg, + "source_node": s, + "source_layer": sl, + "target_node": t, + "target_layer": tl, + "weight": 1.0, + "scaled_weight": 0.5, + "channel": None, + } + + edges = [ + edge("A_L1", "B_L1", "L1", "L1"), + edge("B_L1", "C_L1", "L1", "L1"), + edge("A_L1", "C_L1", "L1", "L1"), + edge("D_L2", "E_L2", "L2", "L2"), + edge("C_L1", "D_L2", "L1", "L2"), # inter-layer + ] + return nodes, edges + + +def _req(**kw): + nodes, edges = _net() + base = dict( + nodes=nodes, + edges=edges, + algorithm="Fruchterman-Reingold", + scope="perLayer", + selected_layers=["L1", "L2"], + ) + base.update(kw) + return LayoutRequest(**base) + + +def test_all_registered_algorithms_run() -> None: + for algo in LAYOUTS: + pos = compute_layout(_req(algorithm=algo)) + assert pos, f"{algo} produced no positions" + for coord in pos.values(): + assert len(coord) == 2 + + +def test_per_layer_excludes_inter_layer_edges() -> None: + # perLayer L1 should place only L1 nodes reachable via intra-layer edges + pos = compute_layout(_req(selected_layers=["L1"])) + assert set(pos) == {"A_L1", "B_L1", "C_L1"} + + +def test_all_layers_scope_places_both_layers() -> None: + pos = compute_layout(_req(scope="allLayers")) + assert {"A_L1", "D_L2"} <= set(pos) + + +def test_no_edge_layout_places_isolated_nodes() -> None: + # add a node with no intra-layer edge; Grid must still place it + nodes, edges = _net() + nodes.append({"id": "Z_L1", "label": "Z", "layer": "L1"}) + req = LayoutRequest( + nodes=nodes, + edges=edges, + algorithm="Grid", + scope="perLayer", + selected_layers=["L1"], + ) + pos = compute_layout(req) + assert "Z_L1" in pos + + +def test_seed_is_deterministic() -> None: + a = compute_layout(_req(algorithm="Fruchterman-Reingold", seed=7)) + b = compute_layout(_req(algorithm="Fruchterman-Reingold", seed=7)) + assert a == b + + +def test_channel_filter_drops_unselected() -> None: + nodes, edges = _net() + for e in edges: + e["channel"] = "keep" + edges[0]["channel"] = "drop" # A_L1-B_L1 + req = LayoutRequest( + nodes=nodes, + edges=edges, + algorithm="Circle", + scope="perLayer", + selected_layers=["L1"], + selected_channels=["keep"], + ) + pos = compute_layout(req) + # A-B edge dropped but A,B still reachable via C's edges → all three present + assert set(pos) == {"A_L1", "B_L1", "C_L1"} + + +def test_unknown_algorithm_400() -> None: + resp = client.post( + "/api/layout", + json={ + "nodes": _net()[0], + "edges": _net()[1], + "algorithm": "Nope", + "scope": "perLayer", + "selected_layers": ["L1"], + }, + ) + assert resp.status_code == 400 + + +def test_endpoint_returns_positions() -> None: + nodes, edges = _net() + resp = client.post( + "/api/layout", + json={ + "nodes": nodes, + "edges": edges, + "algorithm": "Kamada-Kawai", + "scope": "allLayers", + "selected_layers": ["L1", "L2"], + }, + ) + assert resp.status_code == 200 + assert resp.json()["positions"] + + +def test_nodes_per_layer_scope() -> None: + pos = compute_layout( + _req( + scope="nodesPerLayers", + selected_layers=["L1"], + selected_nodes=["A_L1", "B_L1", "C_L1"], + algorithm="Circle", + ) + ) + assert set(pos) <= {"A_L1", "B_L1", "C_L1"} + + +@pytest.mark.parametrize("algo", ["Circle", "Grid", "Random"]) +def test_no_edge_layouts_registered(algo: str) -> None: + assert algo in LAYOUTS diff --git a/functions/igraph/layout.R b/functions/igraph/layout.R deleted file mode 100644 index 1411c33..0000000 --- a/functions/igraph/layout.R +++ /dev/null @@ -1,250 +0,0 @@ -handleLayout <- function() { - tryCatch({ - if (areValidLayoutInputs()) { - renderModal("

Please wait.


Generating layout.

") - callJSHandler("handler_startLoader", T) - - selectedLayerPositions <- input$js_selected_layers + 1 # from JS to R counters - selectedLayerNames <- input$js_layer_names[selectedLayerPositions] - subgraphChoice <- input$subgraphChoice - - runLayoutAlgorithm(selectedLayerNames, subgraphChoice) - } - }, error = function(e) { - print(paste0("Error on layout or clustering: ", e)) - renderError("Error on layout or clustering algorithm.") - }, finally = { - removeModal() - callJSHandler("handler_finishLoader", T) - }) -} - -areValidLayoutInputs <- function() { - areValid <- F - if (existsNetwork()) - if (isLayoutAlgorithmSelected()) - if (existsSelectedLayer()) - areValid <- T - return(areValid) -} - -isLayoutAlgorithmSelected <- function() { - isSelected <- T - if (input$layoutAlgorithmChoice == "-") { - isSelected <- F - renderWarning("Please, select a layout algorithm.") - } - return(isSelected) -} - -runLayoutAlgorithm <- function(selectedLayerNames, subgraphChoice) { - filteredNetworkDF <- filterSelectedChannels(networkDF) - - if (nrow(filteredNetworkDF) > 0) { - set.seed(123) - switch( - subgraphChoice, - "perLayer" = runPerLayerLayout(filteredNetworkDF, selectedLayerNames, - subgraphChoice), - "allLayers" = runAllLayersLayout(filteredNetworkDF, selectedLayerNames, - subgraphChoice), - "nodesPerLayers" = runLocalLayout(filteredNetworkDF, selectedLayerNames, - subgraphChoice) - ) - } -} - -runPerLayerLayout <- function(filteredNetworkDF, selectedLayerNames, - subgraphChoice) { - for (layerName in selectedLayerNames) { - if (input$layoutAlgorithmChoice %in% NO_EDGE_LAYOUTS) - tempFilteredNetworkDF <- filterPseudoNetwork(filteredNetworkDF, layerName) - else - tempFilteredNetworkDF <- filterPerLayer(filteredNetworkDF, layerName) - networkGraph <- parseEdgelistIntoGraph(tempFilteredNetworkDF, subgraphChoice, - layerName) - callJSHandler("handler_setPerLayerFlag", layerName) - applyLayoutWithOptionalClustering(networkGraph) - } -} - -filterPseudoNetwork <- function(filteredNetworkDF, layerNames, - selectedNodeNamesWithLayer = NULL) { - tinyWeight <- min(filteredNetworkDF$ScaledWeight) / 100 - - tempNetwork <- extractSelectedNetworkEdgelist(filteredNetworkDF, layerNames, - selectedNodeNamesWithLayer) - tempNetworkNodesLinked <- - extractSelectedNetworkEdgelistAllLinks(filteredNetworkDF, layerNames, - selectedNodeNamesWithLayer, tinyWeight) - tempNetwork <- rbind(tempNetwork, tempNetworkNodesLinked) - colnames(tempNetwork)[3] <- "weight" - tempGraph <- removeExistingEdges(tempNetwork) - filteredNetworkDF <- parseGraphIntoPseudoFrame(tempGraph) - return(filteredNetworkDF) -} - -extractSelectedNetworkEdgelist <- function(filteredNetworkDF, layerNames, - selectedNodeNamesWithLayer) { - if (is.null(selectedNodeNamesWithLayer)) - tempNetwork <- - filteredNetworkDF[(filteredNetworkDF$SourceLayer %in% layerNames) & - (filteredNetworkDF$TargetLayer %in% layerNames), , drop = F] - else - tempNetwork <- - filteredNetworkDF[(filteredNetworkDF$SourceLayer %in% layerNames) & - (filteredNetworkDF$TargetLayer %in% layerNames) & - (filteredNetworkDF$SourceNode %in% selectedNodeNamesWithLayer) & - (filteredNetworkDF$TargetNode %in% selectedNodeNamesWithLayer), - , drop = F] - tempNetwork <- tempNetwork[, c("SourceNode_Layer", "TargetNode_Layer", - "ScaledWeight")] - return(tempNetwork) -} - -extractSelectedNetworkEdgelistAllLinks <- function(filteredNetworkDF, layerNames, - selectedNodeNamesWithLayer, - tinyWeight) { - tempNetworkDF1 <- - extractSelectedLayerNodes(filteredNetworkDF, "Source", layerNames, - selectedNodeNamesWithLayer) - tempNetworkDF2 <- - extractSelectedLayerNodes(filteredNetworkDF, "Target", layerNames, - selectedNodeNamesWithLayer) - colnames(tempNetworkDF2) <- "SourceNode_Layer" - tempNetworkNodesLinked <- interlinkNodeColumns(tempNetworkDF1, tempNetworkDF2, - tinyWeight) - return(tempNetworkNodesLinked) -} - -extractSelectedLayerNodes <- function(filteredNetworkDF, column, layerNames, - selectedNodeNamesWithLayer) { - if (is.null(selectedNodeNamesWithLayer)) - tempDF <- - filteredNetworkDF[( - filteredNetworkDF[[paste0(column, "Layer")]] %in% layerNames), , drop = F] - else - tempDF <- - filteredNetworkDF[ - (filteredNetworkDF[[paste0(column, "Layer")]] %in% layerNames) & - (filteredNetworkDF[[paste0( - column, "Node_Layer")]] %in% selectedNodeNamesWithLayer), , drop = F] - tempDF <- tempDF[, c(paste0(column, "Node_Layer")), drop = F] - return(tempDF) -} - -interlinkNodeColumns <- function(df1, df2, tinyWeight) { - df <- rbind(df1, df2) - if (nrow(df) > 0) { - df <- unique(df) - df$TargetNode_Layer <- - shiftValuesByOne(df$SourceNode_Layer) - df$ScaledWeight <- tinyWeight - } - return(df) -} - -shiftValuesByOne <- function(vec) { - vec <- c("", vec) - vec[1] <- tail(vec, n = 1) - vec <- head(vec, -1) - return(vec) -} - -removeExistingEdges <- function(networkObject) { - graphObject <- igraph::graph_from_data_frame(networkObject, directed = F) - graphObject <- igraph::simplify(graphObject, remove.multiple = T, - remove.loops = F, edge.attr.comb = "max") - return(graphObject) -} - -parseGraphIntoPseudoFrame <- function(tempGraph) { - filteredNetworkDF <- as.data.frame(igraph::as_edgelist(tempGraph)) - if (nrow(filteredNetworkDF > 0)) { - filteredNetworkDF$weight <- igraph::E(tempGraph)$weight - colnames(filteredNetworkDF) <- c('SourceNode_Layer', 'TargetNode_Layer', - 'weight') - colnames(filteredNetworkDF)[3] <- "ScaledWeight" - } - return(filteredNetworkDF) -} - -applyLayoutWithOptionalClustering <- function(networkGraph) { - if (class(networkGraph) == "igraph") { - if (isClusteringEnabled()) - nodeCoords <- calculateClusteredLayout(networkGraph) - else - nodeCoords <- calculateLayout(networkGraph, input$layoutAlgorithmChoice) - callJSHandler("handler_executeLayout", nodeCoords) - } -} - -calculateLayout <- function(networkGraph, layoutAlgorithm) { - layoutFunc <- getLayoutFunction(layoutAlgorithm) - nodeCoords <- - eval(parse(text = paste0("igraph::", layoutFunc, "(networkGraph)"))) - nodeCoords <- as.data.frame(nodeCoords) - colnames(nodeCoords) <- c("y", "z") - nodeCoords$name <- igraph::V(networkGraph)$name - return(nodeCoords) -} - -getLayoutFunction <- function(layout_name) { - layoutFunction <- switch( - layout_name, - "Reingold-Tilford" = "layout_as_tree", - "Circle" = "layout_in_circle", - "Grid" = "layout_on_grid", - "Random" = "layout_randomly", - "Davidson-Harel" = "layout_with_dh", - "DrL" = "layout_with_drl", - "Fruchterman-Reingold" = "layout_with_fr", - "GEM" = "layout_with_gem", - "Graphopt" = "layout_with_graphopt", - "Kamada-Kawai" = "layout_with_kk", - "Large Graph Layout" = "layout_with_lgl", - "Multidimensional Scaling" = "layout_with_mds", - "Sugiyama" = "layout_with_sugiyama" - ) - return(layoutFunction) -} - -runAllLayersLayout <- function(filteredNetworkDF, selectedLayerNames, - subgraphChoice) { - if (input$layoutAlgorithmChoice %in% NO_EDGE_LAYOUTS) - filteredNetworkDF <- filterPseudoNetwork(filteredNetworkDF, selectedLayerNames) - else - filteredNetworkDF <- filterAllSelectedLayers(filteredNetworkDF, - selectedLayerNames) - - networkGraph <- parseEdgelistIntoGraph(filteredNetworkDF, subgraphChoice, - layerName) - applyLayoutWithOptionalClustering(networkGraph) -} - -runLocalLayout <- function(filteredNetworkDF, selectedLayerNames, - subgraphChoice) { - selectedNodePositions <- input$js_selectedNodePositions + 1 # JS to R iterator - if (existSelectedNodes(selectedNodePositions)) { - nodeNamesWithLayer <- input$js_node_names - selectedNodeNamesWithLayer <- nodeNamesWithLayer[selectedNodePositions] - - if (!input$layoutAlgorithmChoice %in% NO_EDGE_LAYOUTS) - filteredNetworkDF <- filterPerSelectedNodes(filteredNetworkDF, - selectedNodeNamesWithLayer) - for (layerName in selectedLayerNames) { - if (input$layoutAlgorithmChoice %in% NO_EDGE_LAYOUTS) - tempFilteredNetworkDF <- - filterPseudoNetwork(filteredNetworkDF, layerName, - selectedNodeNamesWithLayer) - else - tempFilteredNetworkDF <- filterPerLayer(filteredNetworkDF, layerName) - - networkGraph <- parseEdgelistIntoGraph(tempFilteredNetworkDF, - subgraphChoice, layerName) - # map coordinates on local bounds in executeLayout - callJSHandler("handler_setLocalFlag", T) - applyLayoutWithOptionalClustering(networkGraph) - } - } -} From 1f2c38879edbea3601c3d46c42a910b718dc6ef0 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 23:35:52 +0100 Subject: [PATCH 009/166] backend: clustered layout via supernode strategy (Phase 5) Ports functions/igraph/cluster.R. Clustering is an optional step of POST /api/layout (not a separate endpoint): detect communities (Louvain/Walktrap/Fast Greedy/Label Propagation via Graph.community_*), lay super-nodes out with the global algorithm repelled from origin, lay each community's members locally (real intra edges + tiny all-pairs edges so disconnected members spread), translate into place. Response gains a clusters map. 8 new pytest cases; cluster.R deleted. Co-Authored-By: Claude Fable 5 --- MIGRATION.md | 4 +- backend/app/models/layout.py | 8 ++ backend/app/routers/layout.py | 6 +- backend/app/services/clustering.py | 130 ++++++++++++++++++ backend/app/services/layouts.py | 55 +++++--- backend/tests/test_cluster.py | 115 ++++++++++++++++ backend/tests/test_layout.py | 16 +-- functions/igraph/cluster.R | 210 ----------------------------- 8 files changed, 305 insertions(+), 239 deletions(-) create mode 100644 backend/app/services/clustering.py create mode 100644 backend/tests/test_cluster.py delete mode 100644 functions/igraph/cluster.R diff --git a/MIGRATION.md b/MIGRATION.md index cd7bc4f..8c1b63f 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -23,8 +23,8 @@ See `PLAN.md` for the ordered implementation steps. | `functions/js_handling.R` | *(absorbed into frontend EventBus)* | [ ] | | `functions/edges.R` | `frontend/src/ui/edge.ts` — UI visibility toggles + JS handler calls, no server logic | [ ] | | `functions/vr.R` | `backend/app/routers/vr.py` — PLY + A-Frame HTML generation *(keep/drop decision, PLAN Phase 7)* | [ ] | -| `functions/igraph/general.R` | `backend/app/services/graph.py` | [ ] | -| `functions/igraph/layout.R` | `backend/app/services/layouts.py` | [ ] | +| `functions/igraph/general.R` | `backend/app/services/graph.py` | [~] ported, file kept as spec until Phase 6 | +| `functions/igraph/layout.R` | `backend/app/services/layouts.py` | [x] | | `functions/igraph/cluster.R` | `backend/app/services/clustering.py` — folded into `POST /api/layout` as optional step | [ ] | | `functions/igraph/topology.R` | `backend/app/services/topology.py` | [ ] | diff --git a/backend/app/models/layout.py b/backend/app/models/layout.py index 809adb6..613ad44 100644 --- a/backend/app/models/layout.py +++ b/backend/app/models/layout.py @@ -9,6 +9,11 @@ Scope = Literal["perLayer", "allLayers", "nodesPerLayers"] +class ClusteringOptions(BaseModel): + algorithm: str # Louvain | Walktrap | Fast Greedy | Label Propagation + local_layout: str # layout name for members within a cluster + + class LayoutRequest(BaseModel): nodes: list[NodeModel] edges: list[EdgeModel] @@ -17,9 +22,12 @@ class LayoutRequest(BaseModel): selected_layers: list[str] selected_nodes: list[str] | None = None # Node_Layer ids, for nodesPerLayers selected_channels: list[str] | None = None + clustering: ClusteringOptions | None = None seed: int = 123 # v2 uses set.seed(123) class LayoutResponse(BaseModel): # Node_Layer id -> [y, z] within the layer plane (x is fixed by the layer) positions: dict[str, tuple[float, float]] + # Node_Layer id -> cluster id, only when clustering was requested + clusters: dict[str, int] | None = None diff --git a/backend/app/routers/layout.py b/backend/app/routers/layout.py index 94e56eb..8d8a782 100644 --- a/backend/app/routers/layout.py +++ b/backend/app/routers/layout.py @@ -3,6 +3,7 @@ from fastapi import APIRouter, HTTPException from app.models.layout import LayoutRequest, LayoutResponse +from app.services.clustering import ClusteringError from app.services.layouts import LayoutError, compute_layout router = APIRouter() @@ -11,6 +12,7 @@ @router.post("/api/layout", response_model=LayoutResponse) async def run_layout(req: LayoutRequest) -> LayoutResponse: try: - return LayoutResponse(positions=compute_layout(req)) - except LayoutError as e: + positions, clusters = compute_layout(req) + except (LayoutError, ClusteringError) as e: raise HTTPException(status_code=400, detail=str(e)) from e + return LayoutResponse(positions=positions, clusters=clusters) diff --git a/backend/app/services/clustering.py b/backend/app/services/clustering.py new file mode 100644 index 0000000..d1c81a8 --- /dev/null +++ b/backend/app/services/clustering.py @@ -0,0 +1,130 @@ +"""Clustering + clustered layout — ports functions/igraph/cluster.R. + +Clustering is an optional step of a layout run (v2 calculateClusteredLayout): +detect communities, lay the community super-nodes out with the global algorithm +(repelled from the origin), lay each community's members out locally with the +local algorithm, then translate members into their super-node's position. +""" + +from collections.abc import Callable + +import igraph as ig + +REPELLING_FORCE = 3.0 + +CLUSTERING: dict[str, Callable[[ig.Graph], ig.VertexClustering]] = { + "Louvain": lambda gr: gr.community_multilevel(), + "Walktrap": lambda gr: gr.community_walktrap().as_clustering(), + "Fast Greedy": lambda gr: gr.community_fastgreedy().as_clustering(), + "Label Propagation": lambda gr: gr.community_label_propagation(), +} + + +class ClusteringError(ValueError): + """Unknown clustering algorithm.""" + + +def clustered_layout( + graph: ig.Graph, + cluster_algo: str, + global_layout: Callable[[ig.Graph], ig.Layout], + local_layout: Callable[[ig.Graph], ig.Layout], + is_kamada_kawai: bool, +) -> tuple[dict[str, tuple[float, float]], dict[str, int]]: + """Return ({name: (y, z)}, {name: cluster_id}) for one subgraph. + + Ports execute_strategy3_superNodes_strictPartitioning. + """ + if cluster_algo not in CLUSTERING: + raise ClusteringError(f"Unknown clustering algorithm: {cluster_algo}") + + names = graph.vs["name"] + membership = CLUSTERING[cluster_algo](graph).membership + groups: dict[int, list[str]] = {} + for name, cid in zip(names, membership, strict=True): + groups.setdefault(cid, []).append(name) + + super_coords = _super_node_coords(graph, membership, groups, global_layout) + + weights = graph.es["weight"] if graph.ecount() else [1.0] + tiny = min(weights) / 100 + tiny_w = tiny * REPELLING_FORCE if is_kamada_kawai else tiny / REPELLING_FORCE + + positions: dict[str, tuple[float, float]] = {} + for cid, members in groups.items(): + local = _local_group_coords(graph, membership, cid, members, local_layout, tiny_w) + gx, gy = super_coords[cid] + for name, (x, y) in local.items(): + positions[name] = (x + gx, y + gy) + + clusters = dict(zip(names, membership, strict=True)) + return positions, clusters + + +def _super_node_coords( + graph: ig.Graph, + membership: list[int], + groups: dict[int, list[str]], + global_layout: Callable[[ig.Graph], ig.Layout], +) -> dict[int, tuple[float, float]]: + """Lay out the community graph, then repel every super-node from the origin.""" + cluster_ids = sorted(groups) + idx = {c: i for i, c in enumerate(cluster_ids)} + edges = [(idx[membership[e.source]], idx[membership[e.target]]) for e in graph.es] + sg = ig.Graph(n=len(cluster_ids), edges=edges) + if graph.ecount(): + sg.es["weight"] = graph.es["weight"] + sg.simplify(multiple=True, loops=False, combine_edges={"weight": "mean"}) + else: + sg.simplify(multiple=True, loops=False) + + coords = global_layout(sg) + result: dict[int, tuple[float, float]] = {} + for c in cluster_ids: + x, y = float(coords[idx[c]][0]), float(coords[idx[c]][1]) + a = y / x if x != 0 else y / 0.01 + x *= REPELLING_FORCE + result[c] = (x, a * x) + return result + + +def _local_group_coords( + graph: ig.Graph, + membership: list[int], + cid: int, + members: list[str], + local_layout: Callable[[ig.Graph], ig.Layout], + tiny_w: float, +) -> dict[str, tuple[float, float]]: + """Lay out one community's members: real intra edges + tiny all-pairs edges. + + The tiny complete-graph edges (ports calculateGroupNetworkWithAllEdges) keep + disconnected members spread out; real edges win under simplify(max). + """ + index = {name: i for i, name in enumerate(members)} + edge_pairs: list[tuple[int, int]] = [] + weights: list[float] = [] + + # real intra-community edges + for e in graph.es: + if membership[e.source] == cid and membership[e.target] == cid: + u, v = graph.vs[e.source]["name"], graph.vs[e.target]["name"] + edge_pairs.append((index[u], index[v])) + weights.append(e["weight"]) + + # tiny edges between every pair of members + for i in range(len(members)): + for j in range(i + 1, len(members)): + edge_pairs.append((i, j)) + weights.append(tiny_w) + + lg = ig.Graph(n=len(members), edges=edge_pairs) + lg.vs["name"] = members + if edge_pairs: + lg.es["weight"] = weights + lg.simplify(multiple=True, loops=False, combine_edges={"weight": "max"}) + + coords = local_layout(lg) + return { + lg.vs[i]["name"]: (float(coords[i][0]), float(coords[i][1])) for i in range(lg.vcount()) + } diff --git a/backend/app/services/layouts.py b/backend/app/services/layouts.py index 9695718..7741ca1 100644 --- a/backend/app/services/layouts.py +++ b/backend/app/services/layouts.py @@ -15,6 +15,7 @@ from app.models.layout import LayoutRequest from app.models.network import EdgeModel from app.services import graph as g +from app.services.clustering import clustered_layout LayoutFn = Callable[[ig.Graph], ig.Layout] @@ -37,9 +38,13 @@ class LayoutError(ValueError): """Unknown algorithm or a scope with no computable subgraph.""" -def compute_layout(req: LayoutRequest) -> dict[str, tuple[float, float]]: +def compute_layout( + req: LayoutRequest, +) -> tuple[dict[str, tuple[float, float]], dict[str, int] | None]: if req.algorithm not in LAYOUTS: raise LayoutError(f"Unknown layout algorithm: {req.algorithm}") + if req.clustering and req.clustering.local_layout not in LAYOUTS: + raise LayoutError(f"Unknown local layout: {req.clustering.local_layout}") random.seed(req.seed) # v2 set.seed(123) edges = g.filter_edges(req.edges, req.selected_channels) @@ -52,16 +57,19 @@ def compute_layout(req: LayoutRequest) -> dict[str, tuple[float, float]]: edges = [e for e in edges if e.src in chosen_nodes and e.trg in chosen_nodes] positions: dict[str, tuple[float, float]] = {} - if req.scope == "allLayers": - layer_edges = g.within_layers_edges(edges, selected) - extra = _layer_nodes(req, selected) if no_edge else None - positions.update(_layout_subgraph(layer_edges, req.algorithm, no_edge, extra)) - else: # perLayer or nodesPerLayers — one subgraph per selected layer - for layer in req.selected_layers: - layer_edges = g.intra_layer_edges(edges, layer) - extra = _layer_nodes(req, {layer}) if no_edge else None - positions.update(_layout_subgraph(layer_edges, req.algorithm, no_edge, extra)) - return positions + clusters: dict[str, int] = {} + scopes = [selected] if req.scope == "allLayers" else [{layer} for layer in req.selected_layers] + for layers in scopes: + if req.scope == "allLayers": + layer_edges = g.within_layers_edges(edges, layers) + else: + layer_edges = g.intra_layer_edges(edges, next(iter(layers))) + extra = _layer_nodes(req, layers) if no_edge else None + pos, clu = _layout_subgraph(layer_edges, req, no_edge, extra) + positions.update(pos) + clusters.update(clu) + + return positions, (clusters if req.clustering else None) def _layer_nodes(req: LayoutRequest, layers: set[str]) -> list[str]: @@ -75,17 +83,30 @@ def _layer_nodes(req: LayoutRequest, layers: set[str]) -> list[str]: def _layout_subgraph( edges: list[EdgeModel], - algorithm: str, + req: LayoutRequest, no_edge: bool, extra_vertices: list[str] | None, -) -> dict[str, tuple[float, float]]: +) -> tuple[dict[str, tuple[float, float]], dict[str, int]]: if not edges and not extra_vertices: - return {} # nothing to place (v2: "cannot form a graph") + return {}, {} # nothing to place (v2: "cannot form a graph") # no channels here → remove parallel edges (edge-based scopes rebuild per layer) graph = g.build_graph(edges, remove_multiple=True, extra_vertices=extra_vertices) if graph.vcount() == 0: - return {} - coords = LAYOUTS[algorithm](graph) + return {}, {} + + if req.clustering: + return clustered_layout( + graph, + req.clustering.algorithm, + LAYOUTS[req.algorithm], + LAYOUTS[req.clustering.local_layout], + is_kamada_kawai=req.algorithm == "Kamada-Kawai", + ) + + coords = LAYOUTS[req.algorithm](graph) names = graph.vs["name"] # Sugiyama appends dummy vertices for edge bends — keep only real vertices - return {names[i]: (float(coords[i][0]), float(coords[i][1])) for i in range(graph.vcount())} + positions = { + names[i]: (float(coords[i][0]), float(coords[i][1])) for i in range(graph.vcount()) + } + return positions, {} diff --git a/backend/tests/test_cluster.py b/backend/tests/test_cluster.py new file mode 100644 index 0000000..940c1b3 --- /dev/null +++ b/backend/tests/test_cluster.py @@ -0,0 +1,115 @@ +import pytest +from fastapi.testclient import TestClient + +from app.main import app +from app.models.layout import ClusteringOptions, LayoutRequest +from app.services.clustering import CLUSTERING +from app.services.layouts import compute_layout + +client = TestClient(app) + + +def _two_cluster_net(): + # two 3-cliques joined by a single bridge edge — clear community structure + names = ["A", "B", "C", "D", "E", "F"] + nodes = [{"id": f"{n}_L1", "label": n, "layer": "L1"} for n in names] + + def edge(a, b): + return { + "src": f"{a}_L1", + "trg": f"{b}_L1", + "source_node": a, + "source_layer": "L1", + "target_node": b, + "target_layer": "L1", + "weight": 1.0, + "scaled_weight": 0.5, + "channel": None, + } + + edges = [ + edge("A", "B"), + edge("B", "C"), + edge("A", "C"), + edge("D", "E"), + edge("E", "F"), + edge("D", "F"), + edge("C", "D"), # bridge + ] + return nodes, edges + + +def _req(algo="Louvain", local="Circle"): + nodes, edges = _two_cluster_net() + return LayoutRequest( + nodes=nodes, + edges=edges, + algorithm="Fruchterman-Reingold", + scope="perLayer", + selected_layers=["L1"], + clustering=ClusteringOptions(algorithm=algo, local_layout=local), + ) + + +@pytest.mark.parametrize("algo", list(CLUSTERING)) +def test_every_clustering_algo_runs(algo: str) -> None: + positions, clusters = compute_layout(_req(algo=algo)) + assert clusters is not None + assert set(positions) == {f"{n}_L1" for n in "ABCDEF"} + assert set(clusters) == set(positions) + + +def test_finds_two_communities() -> None: + _, clusters = compute_layout(_req(algo="Louvain")) + assert clusters is not None + # A,B,C in one community; D,E,F in another + assert clusters["A_L1"] == clusters["B_L1"] == clusters["C_L1"] + assert clusters["D_L1"] == clusters["E_L1"] == clusters["F_L1"] + assert clusters["A_L1"] != clusters["D_L1"] + + +def test_no_clustering_returns_none() -> None: + nodes, edges = _two_cluster_net() + _, clusters = compute_layout( + LayoutRequest( + nodes=nodes, + edges=edges, + algorithm="Circle", + scope="perLayer", + selected_layers=["L1"], + ) + ) + assert clusters is None + + +def test_unknown_clustering_400() -> None: + nodes, edges = _two_cluster_net() + resp = client.post( + "/api/layout", + json={ + "nodes": nodes, + "edges": edges, + "algorithm": "Fruchterman-Reingold", + "scope": "perLayer", + "selected_layers": ["L1"], + "clustering": {"algorithm": "Nope", "local_layout": "Circle"}, + }, + ) + assert resp.status_code == 400 + + +def test_endpoint_returns_clusters() -> None: + nodes, edges = _two_cluster_net() + resp = client.post( + "/api/layout", + json={ + "nodes": nodes, + "edges": edges, + "algorithm": "Fruchterman-Reingold", + "scope": "perLayer", + "selected_layers": ["L1"], + "clustering": {"algorithm": "Louvain", "local_layout": "Grid"}, + }, + ) + assert resp.status_code == 200 + assert resp.json()["clusters"] diff --git a/backend/tests/test_layout.py b/backend/tests/test_layout.py index ca3000c..f5971e4 100644 --- a/backend/tests/test_layout.py +++ b/backend/tests/test_layout.py @@ -57,7 +57,7 @@ def _req(**kw): def test_all_registered_algorithms_run() -> None: for algo in LAYOUTS: - pos = compute_layout(_req(algorithm=algo)) + pos, _ = compute_layout(_req(algorithm=algo)) assert pos, f"{algo} produced no positions" for coord in pos.values(): assert len(coord) == 2 @@ -65,12 +65,12 @@ def test_all_registered_algorithms_run() -> None: def test_per_layer_excludes_inter_layer_edges() -> None: # perLayer L1 should place only L1 nodes reachable via intra-layer edges - pos = compute_layout(_req(selected_layers=["L1"])) + pos, _ = compute_layout(_req(selected_layers=["L1"])) assert set(pos) == {"A_L1", "B_L1", "C_L1"} def test_all_layers_scope_places_both_layers() -> None: - pos = compute_layout(_req(scope="allLayers")) + pos, _ = compute_layout(_req(scope="allLayers")) assert {"A_L1", "D_L2"} <= set(pos) @@ -85,13 +85,13 @@ def test_no_edge_layout_places_isolated_nodes() -> None: scope="perLayer", selected_layers=["L1"], ) - pos = compute_layout(req) + pos, _ = compute_layout(req) assert "Z_L1" in pos def test_seed_is_deterministic() -> None: - a = compute_layout(_req(algorithm="Fruchterman-Reingold", seed=7)) - b = compute_layout(_req(algorithm="Fruchterman-Reingold", seed=7)) + a, _ = compute_layout(_req(algorithm="Fruchterman-Reingold", seed=7)) + b, _ = compute_layout(_req(algorithm="Fruchterman-Reingold", seed=7)) assert a == b @@ -108,7 +108,7 @@ def test_channel_filter_drops_unselected() -> None: selected_layers=["L1"], selected_channels=["keep"], ) - pos = compute_layout(req) + pos, _ = compute_layout(req) # A-B edge dropped but A,B still reachable via C's edges → all three present assert set(pos) == {"A_L1", "B_L1", "C_L1"} @@ -144,7 +144,7 @@ def test_endpoint_returns_positions() -> None: def test_nodes_per_layer_scope() -> None: - pos = compute_layout( + pos, _ = compute_layout( _req( scope="nodesPerLayers", selected_layers=["L1"], diff --git a/functions/igraph/cluster.R b/functions/igraph/cluster.R deleted file mode 100644 index e1cc587..0000000 --- a/functions/igraph/cluster.R +++ /dev/null @@ -1,210 +0,0 @@ -handleClusterAlgorithmSelection <- function() { - tryCatch({ - if (input$clusteringAlgorithmChoice != "-") { - shinyjs::show("localLayoutAlgorithmChoice") - } else { - shinyjs::hide("localLayoutAlgorithmChoice") - } - }, error = function(e) { - print(paste0("Error in clustering algorithm selection: ", e)) - renderError("Unexpected error on cluster interface.") - }) -} - -isClusteringEnabled <- function() { - isEnabled <- F - if (input$clusteringAlgorithmChoice != "-") { - isEnabled <- T - if (input$localLayoutAlgorithmChoice == "-") { - isEnabled <- F - renderWarning("You need to select a local layout algorithm for the clusters. - Executing layout without clustering.") - } - } - return(isEnabled) -} - -calculateClusteredLayout <- function(networkGraph) { - assignedClusters <- applyClustering(networkGraph) - globalLayoutFunc <- getLayoutFunction(input$layoutAlgorithmChoice) - localLayoutFunc <- getLayoutFunction(input$localLayoutAlgorithmChoice) - - nodeCoords <- - execute_strategy3_superNodes_strictPartitioning( - networkGraph, assignedClusters, globalLayoutFunc, localLayoutFunc) - return(nodeCoords) -} - -applyClustering <- function(networkGraph) { - clusterFunction <- getClusteringFunction(input$clusteringAlgorithmChoice) - clusteringResults <- - eval(parse(text = paste0("igraph::", clusterFunction, "(networkGraph)"))) - - assignedClusters <- data.frame() - for (i in 1:length(clusteringResults)) { - assignedClusters <- rbind(assignedClusters, cbind(i, paste(clusteringResults[[i]], collapse = ", "))) - } - colnames(assignedClusters) <- c("Annotations", "Nodes") - renderClusteringDF(assignedClusters) - callJSHandler("handler_clickNodeColorPriority", "cluster") - return(assignedClusters) -} - -getClusteringFunction <- function(clustering_name) { - clusterFunction <- switch( - clustering_name, - "Louvain" = "cluster_louvain", - "Walktrap" = "cluster_walktrap", - "Fast Greedy" = "cluster_fast_greedy", - "Label Propagation" = "cluster_label_prop" - ) - return(clusterFunction) -} - -# Strategy 3 #### -execute_strategy3_superNodes_strictPartitioning <- function( - networkGraph, assignedClusters, globalLayoutFunc, localLayoutFunc, - repelling_force = 3) { - subNetwork <- convertGraphToDF(networkGraph) - expandedGroups <- assignedClusters %>% tidyr::separate_rows(Nodes, sep = ", ") - subNetworkWithGroups <- appendClusters(subNetwork, expandedGroups) - groupsGraph <- extractGroupsGraph(subNetworkWithGroups) - superNodeCoords <- calculateSuperNodeCoords(groupsGraph, globalLayoutFunc, - repelling_force) - nodeCoords <- calculateNodeCoordsPerCluster(expandedGroups, subNetworkWithGroups, - superNodeCoords, globalLayoutFunc, - localLayoutFunc, repelling_force) - return(nodeCoords) -} - -convertGraphToDF <- function(networkGraph) { - subNetwork <- as.data.frame(igraph::as_edgelist(networkGraph)) - colnames(subNetwork) <- c('Source', 'Target') - subNetwork$weight <- igraph::E(networkGraph)$weight - return(subNetwork) -} - -appendClusters <- function(subNetwork, expandedGroups) { - subNetworkWithGroups <- merge(subNetwork, expandedGroups, - by.x = 'Source', by.y = 'Nodes', all.x = T) - subNetworkWithGroups <- merge(subNetworkWithGroups, expandedGroups, - by.x = 'Target', by.y = 'Nodes', all.x = T) - subNetworkWithGroups <- - subNetworkWithGroups[, c("Source", "Annotations.x", - "Target", "Annotations.y", "weight")] - colnames(subNetworkWithGroups)[2] <- "SourceGroup" - colnames(subNetworkWithGroups)[4] <- "TargetGroup" - return(subNetworkWithGroups) -} - -extractGroupsGraph <- function(subNetworkWithGroups) { - groupsNetwork <- subNetworkWithGroups[, c("SourceGroup", "TargetGroup", - "weight")] - groupsGraph <- igraph::graph_from_data_frame(groupsNetwork, directed = F) - groupsGraph <- igraph::simplify(groupsGraph, remove.multiple = T, - remove.loops = F, edge.attr.comb = "mean") - return(groupsGraph) -} - -calculateSuperNodeCoords <- function(groupsGraph, globalLayoutFunc, repelling_force) { - superNodeCoords <- eval(parse(text = paste0("igraph::", globalLayoutFunc, "(groupsGraph)"))) - superNodeCoords <- bindNamesToLayout(globalLayoutFunc, superNodeCoords, - groupsGraph) - colnames(superNodeCoords) <- c("x", "y", "superNode") - - # The following will repel all super nodes away from 0,0: - # Foreach node, calculate a = y/x - # Then multiply x by the repeling force and solve for y - superNodeCoords$x <- as.double(superNodeCoords$x) - superNodeCoords$y <- as.double(superNodeCoords$y) - superNodeCoords$a <- ifelse(superNodeCoords$x != 0, superNodeCoords$y / superNodeCoords$x, superNodeCoords$y / 0.01) - superNodeCoords$x <- repelling_force * superNodeCoords$x - superNodeCoords$y <- superNodeCoords$a * superNodeCoords$x - return(superNodeCoords) -} - -bindNamesToLayout <- function(layoutFunc, nodeCoords, graphObject) { - if (layoutFunc == "layout_with_sugiyama") - nodeCoords <- cbind(nodeCoords$layout, names(igraph::V(graphObject))) - else - nodeCoords <- cbind(nodeCoords, names(igraph::V(graphObject))) - nodeCoords <- as.data.frame(nodeCoords) - return(nodeCoords) -} - -# Foreach group, add low-weight within group edges and -# apply local layout on the respective (x,y) superNode coords system -calculateNodeCoordsPerCluster <- function(expandedGroups, subNetworkWithGroups, - superNodeCoords, globalLayoutFunc, - localLayoutFunc, repelling_force) { - interGroupEdges <- merge(expandedGroups, expandedGroups, - by.x = "Annotations", by.y = "Annotations") - tinyWeight <- min(subNetworkWithGroups$weight) / 100 - - nodeCoords <- do.call(rbind, lapply( - unique(expandedGroups$Annotations), function(group) { - groupNetwork <- calculateGroupNetworkWithExistingEdges(subNetworkWithGroups, - group) - groupNetworkAllEdges <- - calculateGroupNetworkWithAllEdges(interGroupEdges, group, globalLayoutFunc, - tinyWeight, repelling_force) - groupNetwork <- rbind(groupNetwork, groupNetworkAllEdges) - groupGraph <- removeExistingEdges(groupNetwork) - tempCoords <- executeLocalLayout(groupGraph, localLayoutFunc, - superNodeCoords, group) - # returns tempCoords to rbind - translateGroupCoords(superNodeCoords, group, tempCoords) - })) - - nodeCoords <- merge(nodeCoords, expandedGroups) - colnames(nodeCoords) <- c("name", "y", "z", "group") - return(nodeCoords) -} - -calculateGroupNetworkWithExistingEdges <- function(subNetworkWithGroups, - group) { - groupNetwork <- subNetworkWithGroups - groupNetwork <- groupNetwork[(groupNetwork$SourceGroup == group & - groupNetwork$TargetGroup == group), ] - groupNetwork <- groupNetwork[, c('Source', 'Target', 'weight')] - return(groupNetwork) -} - -calculateGroupNetworkWithAllEdges <- function(interGroupEdges, group, - globalLayoutFunc, tinyWeight, - repelling_force) { - groupNetworkAllEdges <- - interGroupEdges[interGroupEdges$Annotations == group, ] - tempGraph <- igraph::graph_from_data_frame( - groupNetworkAllEdges[, c("Nodes.x", "Nodes.y")], directed = F) - - if (globalLayoutFunc == 'layout_with_kk') - igraph::E(tempGraph)$weight <- tinyWeight * repelling_force - else - igraph::E(tempGraph)$weight <- tinyWeight / repelling_force - - tempGraph <- igraph::simplify(tempGraph, remove.multiple = T, - remove.loops = T, edge.attr.comb = "first") - groupNetworkAllEdges <- as.data.frame(igraph::as_edgelist(tempGraph)) - groupNetworkAllEdges$weight <- igraph::E(tempGraph)$weight - colnames(groupNetworkAllEdges) <- c('Source', 'Target', 'weight') - return(groupNetworkAllEdges) -} - -executeLocalLayout <- function(groupGraph, localLayoutFunc, superNodeCoords, - group) { - tempCoords <- eval(parse(text = paste0("igraph::", localLayoutFunc, "(groupGraph)"))) - tempCoords <- bindNamesToLayout(localLayoutFunc, tempCoords, groupGraph) - colnames(tempCoords) <- c("x", "y", "Nodes") - tempCoords$x <- as.double(tempCoords$x) - tempCoords$y <- as.double(tempCoords$y) - return(tempCoords) -} - -translateGroupCoords <- function(superNodeCoords, group, tempCoords) { - groupX <- superNodeCoords[superNodeCoords$superNode == group, ]$x - groupY <- superNodeCoords[superNodeCoords$superNode == group, ]$y - tempCoords$x <- tempCoords$x + groupX - tempCoords$y <- tempCoords$y + groupY - return(tempCoords) # gets rbind -} From e0e01d9aac2c5b92542a2b81667a2c34bbadeace Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 23:37:48 +0100 Subject: [PATCH 010/166] backend: POST /api/topology metrics + node scaling (Phase 6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports functions/igraph/topology.R: Degree (raw Graph.degree, not normalized centrality), Clustering Coefficient (weighted local transitivity, isolates=0), Betweenness (edge-direction toggle + weights). Values mapped per subgraph into [0.5, 2.5] via mapper() (constant → 1); raw values returned for the View Data table. 7 pytest cases. topology.R + general.R deleted — functions/igraph/ fully ported. Co-Authored-By: Claude Fable 5 --- MIGRATION.md | 6 +- PLAN.md | 20 ++--- backend/app/main.py | 2 + backend/app/models/topology.py | 27 ++++++ backend/app/routers/topology.py | 17 ++++ backend/app/services/topology.py | 86 ++++++++++++++++++ backend/tests/test_topology.py | 117 +++++++++++++++++++++++++ functions/igraph/general.R | 93 -------------------- functions/igraph/topology.R | 145 ------------------------------- 9 files changed, 262 insertions(+), 251 deletions(-) create mode 100644 backend/app/models/topology.py create mode 100644 backend/app/routers/topology.py create mode 100644 backend/app/services/topology.py create mode 100644 backend/tests/test_topology.py delete mode 100644 functions/igraph/general.R delete mode 100644 functions/igraph/topology.R diff --git a/MIGRATION.md b/MIGRATION.md index 8c1b63f..d4e9bd0 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -23,10 +23,10 @@ See `PLAN.md` for the ordered implementation steps. | `functions/js_handling.R` | *(absorbed into frontend EventBus)* | [ ] | | `functions/edges.R` | `frontend/src/ui/edge.ts` — UI visibility toggles + JS handler calls, no server logic | [ ] | | `functions/vr.R` | `backend/app/routers/vr.py` — PLY + A-Frame HTML generation *(keep/drop decision, PLAN Phase 7)* | [ ] | -| `functions/igraph/general.R` | `backend/app/services/graph.py` | [~] ported, file kept as spec until Phase 6 | +| `functions/igraph/general.R` | `backend/app/services/graph.py` | [x] | | `functions/igraph/layout.R` | `backend/app/services/layouts.py` | [x] | -| `functions/igraph/cluster.R` | `backend/app/services/clustering.py` — folded into `POST /api/layout` as optional step | [ ] | -| `functions/igraph/topology.R` | `backend/app/services/topology.py` | [ ] | +| `functions/igraph/cluster.R` | `backend/app/services/clustering.py` — folded into `POST /api/layout` as optional step | [x] | +| `functions/igraph/topology.R` | `backend/app/services/topology.py` | [x] | --- diff --git a/PLAN.md b/PLAN.md index 23dc86f..45b4c1b 100644 --- a/PLAN.md +++ b/PLAN.md @@ -66,21 +66,21 @@ See `MIGRATION.md` for the old-file → new-file deletion checklist. Clustering in v2 is not a standalone action — it is an option of the layout run (`calculateClusteredLayout()`): cluster, lay out cluster super-nodes globally, lay out members locally, translate into place. No separate `/api/cluster` endpoint. -- [ ] Implement clustering registry (`services/clustering.py`) — Louvain, Walktrap, Fast Greedy, Label Propagation (`Graph.community_*`, all exact with python-igraph) -- [ ] Port supernode strategy (`execute_strategy3_superNodes_strictPartitioning`): global layout on cluster graph with repelling force, per-cluster local layout, coordinate translation -- [ ] Extend `POST /api/layout` with optional `clustering: { algorithm, local_layout }`; response gains `clusters` map (feeds Clustering Data table + node-color priority) -- [ ] Write pytest tests for each clustering algorithm and the clustered layout -- [ ] Delete `functions/igraph/cluster.R` +- [x] Implement clustering registry (`services/clustering.py`) — Louvain, Walktrap, Fast Greedy, Label Propagation (`Graph.community_*`, all exact with python-igraph) +- [x] Port supernode strategy (`execute_strategy3_superNodes_strictPartitioning`): global layout on cluster graph with repelling force, per-cluster local layout, coordinate translation +- [x] Extend `POST /api/layout` with optional `clustering: { algorithm, local_layout }`; response gains `clusters` map (feeds Clustering Data table + node-color priority) +- [x] Write pytest tests for each clustering algorithm and the clustered layout (8 tests, two-community fixture) +- [x] Delete `functions/igraph/cluster.R` --- ## Phase 6 — Backend: Topology Metrics -- [ ] Implement `services/topology.py` — Degree (raw `Graph.degree()`, **not** normalized centrality), Clustering Coefficient (weighted local transitivity, isolates = 0), Betweenness (honours edge-direction toggle + weights) -- [ ] Port `mapper()` — map values into `[TARGET_NODE_SCALE_MIN, TARGET_NODE_SCALE_MAX]` server-side as v2 does; return raw values too for the View Data table -- [ ] Implement `POST /api/topology` router (same scope/filter params as layout) -- [ ] Write pytest tests for each metric with known fixture graphs -- [ ] Delete `functions/igraph/topology.R` +- [x] Implement `services/topology.py` — Degree (raw `Graph.degree(loops=True)`, **not** normalized centrality), Clustering Coefficient (weighted local transitivity, isolates = 0), Betweenness (honours edge-direction toggle + weights) +- [x] Port `mapper()` — map values into `[TARGET_NODE_SCALE_MIN, TARGET_NODE_SCALE_MAX]` per subgraph as v2 does; return raw values too for the View Data table +- [x] Implement `POST /api/topology` router (same scope/filter params as layout) +- [x] Write pytest tests for each metric with known fixture graphs (7 tests, star + triangle fixtures) +- [x] Delete `functions/igraph/topology.R` (+ `general.R` — all igraph ports done, `functions/igraph/` now empty) --- diff --git a/backend/app/main.py b/backend/app/main.py index 85d6e98..e9793a1 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -3,11 +3,13 @@ from app.routers import config as config_router from app.routers import layout as layout_router from app.routers import network as network_router +from app.routers import topology as topology_router app = FastAPI(title="Arena3Dweb API", version="3.0.0") app.include_router(config_router.router) app.include_router(network_router.router) app.include_router(layout_router.router) +app.include_router(topology_router.router) @app.get("/api/health") diff --git a/backend/app/models/topology.py b/backend/app/models/topology.py new file mode 100644 index 0000000..59b8c4e --- /dev/null +++ b/backend/app/models/topology.py @@ -0,0 +1,27 @@ +"""Request/response schemas for POST /api/topology.""" + +from typing import Literal + +from pydantic import BaseModel + +from app.models.network import EdgeModel, NodeModel + +Scope = Literal["perLayer", "allLayers", "nodesPerLayers"] + + +class TopologyRequest(BaseModel): + nodes: list[NodeModel] + edges: list[EdgeModel] + metric: str # Degree | Clustering Coefficient | Betweenness Centrality + scope: Scope = "perLayer" + selected_layers: list[str] + selected_nodes: list[str] | None = None + selected_channels: list[str] | None = None + directed: bool = False # v2 edgeDirectionToggle (affects betweenness) + + +class TopologyResponse(BaseModel): + # Node_Layer id -> node scale mapped into [0.5, 2.5] + scales: dict[str, float] + # Node_Layer id -> raw metric value (for the View Data table) + raw: dict[str, float] diff --git a/backend/app/routers/topology.py b/backend/app/routers/topology.py new file mode 100644 index 0000000..1316d83 --- /dev/null +++ b/backend/app/routers/topology.py @@ -0,0 +1,17 @@ +"""POST /api/topology — replaces functions/igraph/topology.R.""" + +from fastapi import APIRouter, HTTPException + +from app.models.topology import TopologyRequest, TopologyResponse +from app.services.topology import TopologyError, compute_topology + +router = APIRouter() + + +@router.post("/api/topology", response_model=TopologyResponse) +async def run_topology(req: TopologyRequest) -> TopologyResponse: + try: + scales, raw = compute_topology(req) + except TopologyError as e: + raise HTTPException(status_code=400, detail=str(e)) from e + return TopologyResponse(scales=scales, raw=raw) diff --git a/backend/app/services/topology.py b/backend/app/services/topology.py new file mode 100644 index 0000000..6ec3059 --- /dev/null +++ b/backend/app/services/topology.py @@ -0,0 +1,86 @@ +"""Topology metrics + node scaling — ports functions/igraph/topology.R. + +Three metrics (raw igraph values, not normalized centralities), then per-subgraph +mapping into [TARGET_NODE_SCALE_MIN, TARGET_NODE_SCALE_MAX] as v2's scaleTopology does. +""" + +import igraph as ig + +from app import config +from app.models.topology import TopologyRequest +from app.services import graph as g + + +class TopologyError(ValueError): + """Unknown metric.""" + + +def _degree(graph: ig.Graph, directed: bool) -> list[float]: + # v2: degree(mode="all", loops=T, normalized=F) — raw degree, self-loops counted + return [float(d) for d in graph.degree(loops=True)] + + +def _clustering_coefficient(graph: ig.Graph, directed: bool) -> list[float]: + # v2: transitivity(type="weighted", isolates="zero") + vals = graph.transitivity_local_undirected(weights="weight", mode="zero") + return [float(v) for v in vals] + + +def _betweenness(graph: ig.Graph, directed: bool) -> list[float]: + # v2: betweenness(directed=edgeDirectionToggle, weights=E$weight, normalized=F) + weights = graph.es["weight"] if graph.ecount() else None + return [float(b) for b in graph.betweenness(directed=directed, weights=weights)] + + +METRICS = { + "Degree": _degree, + "Clustering Coefficient": _clustering_coefficient, + "Betweenness Centrality": _betweenness, +} + + +def _mapper(values: list[float], out_min: float, out_max: float, default: float) -> list[float]: + """Linear-rescale into [out_min, out_max]; constant input → default. + + Ports functions/general.R mapper() (topology uses default 1). + """ + lo, hi = min(values), max(values) + if hi - lo == 0: + return [default] * len(values) + return [(v - lo) * (out_max - out_min) / (hi - lo) + out_min for v in values] + + +def compute_topology(req: TopologyRequest) -> tuple[dict[str, float], dict[str, float]]: + if req.metric not in METRICS: + raise TopologyError(f"Unknown topology metric: {req.metric}") + + edges = g.filter_edges(req.edges, req.selected_channels) + if req.scope == "nodesPerLayers": + chosen = set(req.selected_nodes or []) + edges = [e for e in edges if e.src in chosen and e.trg in chosen] + + selected = set(req.selected_layers) + if req.scope == "allLayers": + scopes = [selected] + else: + scopes = [{layer} for layer in req.selected_layers] + + scales: dict[str, float] = {} + raw: dict[str, float] = {} + for layers in scopes: + if req.scope == "allLayers": + layer_edges = g.within_layers_edges(edges, layers) + else: + layer_edges = g.intra_layer_edges(edges, next(iter(layers))) + if len(layer_edges) < 2: + continue # v2: fewer than 2 edges cannot form a graph + graph = g.build_graph(layer_edges, remove_multiple=True) + names = graph.vs["name"] + values = METRICS[req.metric](graph, req.directed) + mapped = _mapper( + values, config.TARGET_NODE_SCALE_MIN, config.TARGET_NODE_SCALE_MAX, default=1.0 + ) + for name, r, m in zip(names, values, mapped, strict=True): + raw[name] = r + scales[name] = m + return scales, raw diff --git a/backend/tests/test_topology.py b/backend/tests/test_topology.py new file mode 100644 index 0000000..28704db --- /dev/null +++ b/backend/tests/test_topology.py @@ -0,0 +1,117 @@ +import pytest +from fastapi.testclient import TestClient + +from app.main import app +from app.models.topology import TopologyRequest +from app.services.topology import METRICS, compute_topology + +client = TestClient(app) + + +def _star_net(): + # star: hub H connected to A,B,C in L1 → H has degree 3, leaves degree 1 + nodes = [{"id": f"{n}_L1", "label": n, "layer": "L1"} for n in ("H", "A", "B", "C")] + + def edge(a, b): + return { + "src": f"{a}_L1", + "trg": f"{b}_L1", + "source_node": a, + "source_layer": "L1", + "target_node": b, + "target_layer": "L1", + "weight": 1.0, + "scaled_weight": 0.5, + "channel": None, + } + + edges = [edge("H", "A"), edge("H", "B"), edge("H", "C")] + return nodes, edges + + +def _req(metric="Degree", **kw): + nodes, edges = _star_net() + base = dict(nodes=nodes, edges=edges, metric=metric, scope="perLayer", selected_layers=["L1"]) + base.update(kw) + return TopologyRequest(**base) + + +@pytest.mark.parametrize("metric", list(METRICS)) +def test_every_metric_runs(metric: str) -> None: + scales, raw = compute_topology(_req(metric=metric)) + assert set(scales) == {"H_L1", "A_L1", "B_L1", "C_L1"} + assert set(raw) == set(scales) + + +def test_degree_is_raw_not_normalized() -> None: + _, raw = compute_topology(_req(metric="Degree")) + assert raw["H_L1"] == 3.0 # raw degree, not centrality (which would be 1.0) + assert raw["A_L1"] == 1.0 + + +def test_scales_mapped_into_target_range() -> None: + scales, _ = compute_topology(_req(metric="Degree")) + assert min(scales.values()) == pytest.approx(0.5) + assert max(scales.values()) == pytest.approx(2.5) + + +def test_constant_metric_defaults_to_one() -> None: + # a triangle: every node has degree 2 → constant → scale defaults to 1 + nodes = [{"id": f"{n}_L1", "label": n, "layer": "L1"} for n in ("A", "B", "C")] + + def edge(a, b): + return { + "src": f"{a}_L1", + "trg": f"{b}_L1", + "source_node": a, + "source_layer": "L1", + "target_node": b, + "target_layer": "L1", + "weight": 1.0, + "scaled_weight": 0.5, + "channel": None, + } + + edges = [edge("A", "B"), edge("B", "C"), edge("A", "C")] + scales, _ = compute_topology( + TopologyRequest( + nodes=nodes, + edges=edges, + metric="Degree", + scope="perLayer", + selected_layers=["L1"], + ) + ) + assert all(v == pytest.approx(1.0) for v in scales.values()) + + +def test_unknown_metric_400() -> None: + nodes, edges = _star_net() + resp = client.post( + "/api/topology", + json={ + "nodes": nodes, + "edges": edges, + "metric": "Nope", + "scope": "perLayer", + "selected_layers": ["L1"], + }, + ) + assert resp.status_code == 400 + + +def test_endpoint_returns_scales_and_raw() -> None: + nodes, edges = _star_net() + resp = client.post( + "/api/topology", + json={ + "nodes": nodes, + "edges": edges, + "metric": "Betweenness Centrality", + "scope": "perLayer", + "selected_layers": ["L1"], + }, + ) + assert resp.status_code == 200 + body = resp.json() + assert body["scales"] and body["raw"] diff --git a/functions/igraph/general.R b/functions/igraph/general.R deleted file mode 100644 index 40f5e57..0000000 --- a/functions/igraph/general.R +++ /dev/null @@ -1,93 +0,0 @@ -existsNetwork <- function(silent = F) { - exist <- T - if (nrow(networkDF) == 0) { - exist <- F - if (!silent) - renderWarning("Upload/import a network first through the File tab.") - } - return(exist) -} - -existsSelectedLayer <- function() { - exist <- T - if (is.null(input$js_selected_layers)) { - exist <- F - renderWarning("Select at least one layer.") - } - return(exist) -} - -filterSelectedChannels <- function(netData) { - if ("Channel" %in% colnames(netData)) { - netData <- netData[(netData$Channel %in% input$js_selectedChannels), , drop = F] - if (is.null(input$js_selectedChannels)) - renderWarning("Select at least one channel.") - } - return(netData) -} - -filterPerLayer <- function(netData, layerName) { - filteredNetworkDF <- netData[(netData$SourceLayer == layerName) & - (netData$TargetLayer == layerName), , drop = F] - return(filteredNetworkDF) -} - -filterAllSelectedLayers <- function(netData, selectedLayerNames) { - filteredNetworkDF <- - netData[(netData$SourceLayer %in% selectedLayerNames) & - (netData$TargetLayer %in% selectedLayerNames), , drop = F] - return(filteredNetworkDF) -} - -filterPerSelectedNodes <- function(netData, selectedNodeNamesWithLayer) { - filteredNetworkDF <- - netData[(netData$SourceNode_Layer %in% selectedNodeNamesWithLayer) & - (netData$TargetNode_Layer %in% selectedNodeNamesWithLayer), , - drop = F] - return(filteredNetworkDF) -} - -parseEdgelistIntoGraph <- function(filteredNetworkDF, subgraphChoice, layerName) { - networkGraph <- "" - if (isIGraphObjectValid(filteredNetworkDF, subgraphChoice, layerName)) { - networkEdgelist <- filteredNetworkDF[, c("SourceNode_Layer", - "TargetNode_Layer", "ScaledWeight")] - # distinct, in case multiple rows remained in multi-channel nets - networkEdgelist <- dplyr::distinct(networkEdgelist, SourceNode_Layer, - TargetNode_Layer, .keep_all = T) - networkGraph <- createGraph(networkEdgelist) - } - return(networkGraph) -} - -isIGraphObjectValid <- function(filteredNetworkDF, subgraphChoice, layerName) { - isValid <- T - if (nrow(filteredNetworkDF) < 2) { - isValid <- F - if (subgraphChoice == "allLayers") - renderWarning(paste0("Subgraph of selected layers cannot form a graph.")) - else - renderWarning(paste0("Layer ", layerName, " cannot form a graph.")) - } - return(isValid) -} - -createGraph <- function(edgelist) { - graph <- igraph::graph_from_data_frame(edgelist, directed = F) - igraph::E(graph)$weight <- as.double(edgelist[, 'ScaledWeight']) - # if it does not have channels remove multiple edges else not - removeMultiple <- is.na(input$js_selectedChannels) - # remove loops and multiple edges, simplify sum aggregates same edges - graph <- igraph::simplify(graph, remove.multiple = removeMultiple, - remove.loops = F, edge.attr.comb = "sum") - return(graph) -} - -existSelectedNodes <- function(selectedNodePositions) { - exist <- T - if (length(selectedNodePositions) == 0) { - exist <- F - renderWarning("Cannot execute local layouts without selected nodes.") - } - return(exist) -} diff --git a/functions/igraph/topology.R b/functions/igraph/topology.R deleted file mode 100644 index 3e8d04a..0000000 --- a/functions/igraph/topology.R +++ /dev/null @@ -1,145 +0,0 @@ -handleTopologyScaling <- function() { - tryCatch({ - if (areValidTopologyScalingInputs()) { - renderModal("

Please wait.


Rescaling nodes.

") - callJSHandler("handler_startLoader", T) - - selectedLayerPositions <- input$js_selected_layers + 1 # from JS to R counters - selectedLayerNames <- input$js_layer_names[selectedLayerPositions] - subgraphChoice <- input$subgraphChoice - - runTopologyScaling(selectedLayerNames, subgraphChoice) - } - }, error = function(e) { - print(paste0("Error in topological scaling: ", e)) - renderError("Unexpected topological scaling error.") - }, finally = { - removeModal() - callJSHandler("handler_finishLoader", T) - }) -} - -areValidTopologyScalingInputs <- function() { - areValid <- F - if (existsNetwork()) - if (isTopologyMetricSelected()) - if (existsSelectedLayer()) - areValid <- T - return(areValid) -} - -isTopologyMetricSelected <- function() { - isSelected <- T - if (input$topologyScaleMetricChoice == "-") { - isSelected <- F - renderWarning("Please, select a topology metric.") - } - return(isSelected) -} - -runTopologyScaling <- function(selectedLayerNames, subgraphChoice) { - filteredNetworkDF <- filterSelectedChannels(networkDF) - - if (nrow(filteredNetworkDF) > 0) { - switch( - subgraphChoice, - "perLayer" = runPerLayerScaling(filteredNetworkDF, selectedLayerNames, - subgraphChoice), - "allLayers" = runAllLayersScaling(filteredNetworkDF, selectedLayerNames, - subgraphChoice), - "nodesPerLayers" = runLocalScaling(filteredNetworkDF, selectedLayerNames, - subgraphChoice) - ) - } -} - -runPerLayerScaling <- function(filteredNetworkDF, selectedLayerNames, - subgraphChoice) { - for (layerName in selectedLayerNames) { - tempFilteredNetworkDF <- filterPerLayer(filteredNetworkDF, layerName) - networkGraph <- parseEdgelistIntoGraph(tempFilteredNetworkDF, subgraphChoice, - layerName) - scaleTopology(networkGraph) - } -} - -scaleTopology <- function(networkGraph) { - if (class(networkGraph) == "igraph") { - topologyMetricChoice <- input$topologyScaleMetricChoice - - scale <- switch( - topologyMetricChoice, - "Degree" = - igraph::degree(networkGraph, v = igraph::V(networkGraph), mode = "all", loops = T, - normalized = F), - "Clustering Coefficient" = { - scale <- igraph::transitivity(networkGraph, type = "weighted", vids = NULL, - weights = NULL, isolates = "zero") # NULL: E(networkGraph)$weight - names(scale) <- igraph::V(networkGraph)$name - scale - }, - "Betweenness Centrality" = - igraph::betweenness(networkGraph, v = igraph::V(networkGraph), - directed = input$edgeDirectionToggle, weights = NULL, # NULL == E(networkGraph)$weight - normalized = F) - ) - nodeScale <- calculateNodeScaleDF(scale) - prepareMetricTable(topologyMetricChoice, nodeScale) - nodeScale$scale <- mapper(nodeScale$scale, - TARGET_NODE_SCALE_MIN, TARGET_NODE_SCALE_MAX, - defaultValue = 1) - callJSHandler("handler_scaleTopology", nodeScale) - } -} - -prepareMetricTable <- function(topologyMetricChoice, nodeScale) { - colnames(nodeScale) <- c("Node", topologyMetricChoice) - nodeScale <- - nodeScale[order(-nodeScale[[topologyMetricChoice]]), ] - nodeScale[[topologyMetricChoice]] <- - format(round(nodeScale[[topologyMetricChoice]], 2)) - nodeScale$Layer <- extractColumnFrom_node_layerDF(nodeScale$Node, "Layer") - nodeScale$Node <- extractColumnFrom_node_layerDF(nodeScale$Node, "Node") - nodeScale <- nodeScale[, c("Node", "Layer", topologyMetricChoice)] - - metric <- switch( - topologyMetricChoice, - "Degree" = "degree", - "Clustering Coefficient" = "transitivity", - "Betweenness Centrality" = "betweenness" - ) - renderMetricTable(topologyMetricChoice, nodeScale, metric) -} - -calculateNodeScaleDF <- function(scale) { - nodeScale <- as.data.frame(scale) - nodeScale$nodeName <- rownames(nodeScale) - rownames(nodeScale) <- NULL - nodeScale <- nodeScale[, c("nodeName", "scale")] - return(nodeScale) -} - -runAllLayersScaling <- function(filteredNetworkDF, selectedLayerNames, - subgraphChoice) { - filteredNetworkDF <- filterAllSelectedLayers(filteredNetworkDF, selectedLayerNames) - networkGraph <- parseEdgelistIntoGraph(filteredNetworkDF, subgraphChoice, - layerName) - scaleTopology(networkGraph) -} - -runLocalScaling <- function(filteredNetworkDF, selectedLayerNames, - subgraphChoice) { - selectedNodePositions <- input$js_selectedNodePositions + 1 # JS to R iterator - if (existSelectedNodes(selectedNodePositions)) { - nodeNamesWithLayer <- input$js_node_names - selectedNodeNamesWithLayer <- nodeNamesWithLayer[selectedNodePositions] - filterPerSelectedNodes <- filterPerSelectedNodes(filteredNetworkDF, - selectedNodeNamesWithLayer) - for (layerName in selectedLayerNames) { - tempFilteredNetworkDF <- filterPerLayer(filterPerSelectedNodes, layerName) - networkGraph <- parseEdgelistIntoGraph(tempFilteredNetworkDF, - subgraphChoice, layerName) - scaleTopology(networkGraph) - } - } -} From 30298df8c34f457a4fdce90abbc896af1c5b4d51 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 23:41:43 +0100 Subject: [PATCH 011/166] backend: session import/export + external API (Phase 7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - POST /api/session/import: ports parseUploadedJSON + isJSONValid (defaults for scene/layers/nodes/edges, channel drop, edge dedup, scramble flag). Loads real Arena3DwebApp_aspirin.json / export JSONs. - POST /api/session/export: thin JSON download (stateless — frontend owns the state). - POST /api/external + GET /api/external/{token}: session stored under a random token in tmp/ with 24h TTL sweep + path-traversal guard; resolves back through the same normalizer (SPEC §5). - VR (functions/vr.R) DROPPED, not ported — depended on external bib.fleming.gr hosting + A-Frame, out of scope (see MIGRATION.md). 11 pytest cases. Deleted init.R, general.R, reset.R, render.R, js_handling.R, vr.R. input.R kept: attribute-upload functions still to port to a future /api/attributes endpoint. Co-Authored-By: Claude Fable 5 --- MIGRATION.md | 16 ++-- PLAN.md | 15 ++-- backend/app/main.py | 4 + backend/app/models/session.py | 24 ++++++ backend/app/routers/external.py | 61 +++++++++++++++ backend/app/routers/session.py | 37 +++++++++ backend/app/services/session.py | 135 ++++++++++++++++++++++++++++++++ backend/tests/test_session.py | 114 +++++++++++++++++++++++++++ functions/general.R | 32 -------- functions/init.R | 34 -------- functions/js_handling.R | 24 ------ functions/render.R | 85 -------------------- functions/reset.R | 44 ----------- functions/vr.R | 113 -------------------------- 14 files changed, 392 insertions(+), 346 deletions(-) create mode 100644 backend/app/models/session.py create mode 100644 backend/app/routers/external.py create mode 100644 backend/app/routers/session.py create mode 100644 backend/app/services/session.py create mode 100644 backend/tests/test_session.py delete mode 100644 functions/general.R delete mode 100644 functions/init.R delete mode 100644 functions/js_handling.R delete mode 100644 functions/render.R delete mode 100644 functions/reset.R delete mode 100644 functions/vr.R diff --git a/MIGRATION.md b/MIGRATION.md index d4e9bd0..5cefb3b 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -15,14 +15,16 @@ See `PLAN.md` for the ordered implementation steps. | `config/global_variables.R` | `backend/app/config.py` | [x] | | `config/static_variables.R` | *(mutable Shiny state — no equivalent in stateless server)* | [x] | | `config/ui_variables.R` | *(absorbed into frontend config — see Removed)* | [x] | -| `functions/input.R` | `backend/app/services/parser.py` + `backend/app/routers/network.py` | [ ] | -| `functions/init.R` | `backend/app/routers/config.py` + `backend/app/main.py` | [ ] | -| `functions/general.R` | `backend/app/services/parser.py` (file read) + `backend/app/services/topology.py` (`mapper()`) — JS-bridge helpers die with Shiny | [ ] | -| `functions/reset.R` | *(stateless server — no equivalent needed)* | [ ] | -| `functions/render.R` | *(absorbed into FastAPI error responses)* | [ ] | -| `functions/js_handling.R` | *(absorbed into frontend EventBus)* | [ ] | +| `functions/input.R` (TSV upload) | `backend/app/services/parser.py` + `routers/network.py` | [x] | +| `functions/input.R` (JSON import + export) | `backend/app/services/session.py` + `routers/session.py` | [x] | +| `functions/input.R` (attribute uploads) | *(not yet ported → future `POST /api/attributes`)* | [ ] | +| `functions/init.R` | `backend/app/routers/config.py` + `backend/app/main.py` | [x] | +| `functions/general.R` | `backend/app/services/parser.py` (file read) + `topology.py`/`parser.py` (`mapper()`) — JS-bridge helpers die with Shiny | [x] | +| `functions/reset.R` | *(stateless server — no equivalent needed)* | [x] | +| `functions/render.R` | *(absorbed into FastAPI error responses)* | [x] | +| `functions/js_handling.R` | *(absorbed into frontend EventBus)* | [x] | | `functions/edges.R` | `frontend/src/ui/edge.ts` — UI visibility toggles + JS handler calls, no server logic | [ ] | -| `functions/vr.R` | `backend/app/routers/vr.py` — PLY + A-Frame HTML generation *(keep/drop decision, PLAN Phase 7)* | [ ] | +| `functions/vr.R` | **DROPPED** — VR mode not ported (depended on external `bib.fleming.gr` hosting + A-Frame; niche, out of scope) | [x] | | `functions/igraph/general.R` | `backend/app/services/graph.py` | [x] | | `functions/igraph/layout.R` | `backend/app/services/layouts.py` | [x] | | `functions/igraph/cluster.R` | `backend/app/services/clustering.py` — folded into `POST /api/layout` as optional step | [x] | diff --git a/PLAN.md b/PLAN.md index 45b4c1b..4af20d1 100644 --- a/PLAN.md +++ b/PLAN.md @@ -86,13 +86,14 @@ Clustering in v2 is not a standalone action — it is an option of the layout ru ## Phase 7 — Backend: Session & External API -- [ ] Implement Pydantic models for session (`models/session.py`) -- [ ] Implement `POST /api/session/import` router — port `importNetwork()` -- [ ] Implement `POST /api/session/export` router — port `convertSessionToJSON()` -- [ ] Implement `POST /api/external` + `GET /api/external/` — port `resolveAPI()`; token = short random ID, session JSON stored in `tmp/` with TTL cleanup (base64-in-URL rejected — 10k-edge sessions exceed URL limits, see SPEC §5) -- [ ] Decide VR feature: port `functions/vr.R` → `routers/vr.py` (PLY + A-Frame HTML written to `tmp/`, served by `GET /api/vr/`) or drop with changelog note — v2 depends on external hosting at `bib.fleming.gr` -- [ ] Write pytest tests for all session and external endpoints using `www/data/*.json` as fixtures -- [ ] Delete `functions/init.R`, `functions/general.R`, `functions/reset.R`, `functions/vr.R`, `functions/render.R`, `functions/js_handling.R` (`functions/edges.R` is UI logic — dies with `views/` in Phase 13) +- [x] Implement Pydantic models for session (`models/session.py`) +- [x] Implement `POST /api/session/import` router — ports `parseUploadedJSON()` + `isJSONValid()` (defaults for scene/layers/nodes/edges, channel handling, edge dedup, scramble flag) +- [x] Implement `POST /api/session/export` router — thin packaging (stateless: frontend holds state, server returns JSON download) +- [x] Implement `POST /api/external` + `GET /api/external/` — token = `secrets.token_urlsafe`, session JSON stored in `tmp/` with 24h TTL sweep, path-traversal guard on resolve +- [x] **VR: dropped.** `functions/vr.R` deleted, not ported. v2's VR mode wrote PLY + A-Frame HTML served from external `bib.fleming.gr` infra; niche feature, out of scope for the restack. Recorded in MIGRATION.md. +- [x] Write pytest tests for all session and external endpoints using `www/data/*.json` as fixtures (11 tests, real `Arena3DwebApp_aspirin.json` + `figure1_export.json`) +- [x] Delete `functions/init.R`, `functions/general.R`, `functions/reset.R`, `functions/vr.R`, `functions/render.R`, `functions/js_handling.R` (`functions/edges.R` is UI logic — dies with `views/` in Phase 13) +- [ ] **Remaining in `functions/input.R`**: node/edge attribute-file uploads (`handleInputNodeAttributeFileUpload`, `handleInputEdgeAttributeFileUpload`) — not yet ported; map to a future `POST /api/attributes` endpoint. `input.R` deletion deferred until then. --- diff --git a/backend/app/main.py b/backend/app/main.py index e9793a1..f4c7187 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,8 +1,10 @@ from fastapi import FastAPI from app.routers import config as config_router +from app.routers import external as external_router from app.routers import layout as layout_router from app.routers import network as network_router +from app.routers import session as session_router from app.routers import topology as topology_router app = FastAPI(title="Arena3Dweb API", version="3.0.0") @@ -10,6 +12,8 @@ app.include_router(network_router.router) app.include_router(layout_router.router) app.include_router(topology_router.router) +app.include_router(session_router.router) +app.include_router(external_router.router) @app.get("/api/health") diff --git a/backend/app/models/session.py b/backend/app/models/session.py new file mode 100644 index 0000000..c8d80d0 --- /dev/null +++ b/backend/app/models/session.py @@ -0,0 +1,24 @@ +"""Session schemas. JSON is loose/stringly-typed (ports v2 export format), so the +import model is permissive — validation + defaulting lives in services/session.py.""" + +from typing import Any + +from pydantic import BaseModel + + +class SessionImportResponse(BaseModel): + """Normalized session returned to the frontend (ports parseUploadedJSON output).""" + + scene: dict[str, Any] + layers: list[dict[str, Any]] + nodes: list[dict[str, Any]] + edges: list[dict[str, Any]] + universalLabelColor: str + direction: bool + edgeOpacityByWeight: bool + scramble_nodes: bool + + +class ExternalCreateResponse(BaseModel): + token: str + url: str diff --git a/backend/app/routers/external.py b/backend/app/routers/external.py new file mode 100644 index 0000000..bdb9d4f --- /dev/null +++ b/backend/app/routers/external.py @@ -0,0 +1,61 @@ +"""External API — ports resolveAPI(). A caller POSTs a session; we store it under a +random token in tmp/ (TTL-swept) and hand back a self-contained URL. GET resolves the +token back to a normalized session, same as a file import. + +Token-in-file (not base64-in-URL) because MAX_EDGES=10k sessions blow past URL limits +(SPEC §5). These tmp files are the one deliberate exception to the stateless server. +""" + +import json +import os +import secrets +import time +from typing import Any + +from fastapi import APIRouter, HTTPException + +from app import config +from app.models.session import ExternalCreateResponse, SessionImportResponse +from app.services.session import SessionValidationError, normalize_session + +router = APIRouter() + +TTL_SECONDS = 24 * 60 * 60 +_BASE_URL = os.environ.get("ARENA_PUBLIC_URL", "") + + +def _sweep() -> None: + """Delete stored sessions older than the TTL (ports the tmp cleanup intent).""" + if not os.path.isdir(config.TMP_PATH): + return + cutoff = time.time() - TTL_SECONDS + for name in os.listdir(config.TMP_PATH): + path = os.path.join(config.TMP_PATH, name) + if name.endswith(".json") and os.path.getmtime(path) < cutoff: + os.remove(path) + + +@router.post("/api/external", response_model=ExternalCreateResponse) +async def create_external(session: dict[str, Any]) -> ExternalCreateResponse: + os.makedirs(config.TMP_PATH, exist_ok=True) + _sweep() + token = secrets.token_urlsafe(16) + with open(os.path.join(config.TMP_PATH, f"{token}.json"), "w") as fh: + json.dump(session, fh) + return ExternalCreateResponse(token=token, url=f"{_BASE_URL}/?session={token}") + + +@router.get("/api/external/{token}", response_model=SessionImportResponse) +async def resolve_external(token: str) -> SessionImportResponse: + # guard against path traversal — tokens are urlsafe base64, never contain / or . + if not token.isalnum() and not all(c.isalnum() or c in "-_" for c in token): + raise HTTPException(status_code=400, detail="Invalid token.") + path = os.path.join(config.TMP_PATH, f"{token}.json") + if not os.path.isfile(path): + raise HTTPException(status_code=404, detail="Session not found or expired.") + with open(path) as fh: + data = json.load(fh) + try: + return SessionImportResponse(**normalize_session(data)) + except SessionValidationError as e: + raise HTTPException(status_code=400, detail=str(e)) from e diff --git a/backend/app/routers/session.py b/backend/app/routers/session.py new file mode 100644 index 0000000..a3eba4c --- /dev/null +++ b/backend/app/routers/session.py @@ -0,0 +1,37 @@ +"""Session import/export — ports loadNetworkFromJSONFilepath() and +convertSessionToJSON() from functions/input.R. + +Export is a thin packaging endpoint: in the stateless design the frontend already +holds the full scene state, so the server just returns it as a JSON download. +""" + +import json +from typing import Any + +from fastapi import APIRouter, HTTPException, UploadFile +from fastapi.responses import JSONResponse + +from app.models.session import SessionImportResponse +from app.services.session import SessionValidationError, normalize_session + +router = APIRouter() + + +@router.post("/api/session/import", response_model=SessionImportResponse) +async def import_session(file: UploadFile) -> SessionImportResponse: + try: + data = json.loads((await file.read()).decode("utf-8", errors="replace")) + except json.JSONDecodeError as e: + raise HTTPException(status_code=400, detail="Bad imported network file format.") from e + try: + return SessionImportResponse(**normalize_session(data)) + except SessionValidationError as e: + raise HTTPException(status_code=400, detail=str(e)) from e + + +@router.post("/api/session/export") +async def export_session(session: dict[str, Any]) -> JSONResponse: + return JSONResponse( + content=session, + headers={"Content-Disposition": 'attachment; filename="network.json"'}, + ) diff --git a/backend/app/services/session.py b/backend/app/services/session.py new file mode 100644 index 0000000..d50bd25 --- /dev/null +++ b/backend/app/services/session.py @@ -0,0 +1,135 @@ +"""Session JSON import normalization — ports the JSON path of functions/input.R +(loadNetworkFromJSONFilepath → parseUploadedJSON + isJSONValid). + +Fills defaults for scene/layers/nodes/edges, handles channels, dedups edges, +and decides the scramble/generate-coordinate flags — so a partial JSON (e.g. an +external-API payload with only src/trg) renders like v2. +""" + +from typing import Any + +from app import config + + +class SessionValidationError(ValueError): + """A blocking JSON-format failure.""" + + +def _empty(v: Any) -> bool: + return v is None or v == "" or (isinstance(v, str) and v.strip() == "") + + +def _default(value: Any, default: Any = "0") -> Any: + """Ports keepValuesOrDefault: fill None/empty with default, coerce TRUE/FALSE.""" + if value is None: + return default + if value == "TRUE": + return True + if value == "FALSE": + return False + if isinstance(value, list): + return [default if _empty(x) else x for x in value] + return default if _empty(value) else value + + +def _validate(data: dict[str, Any]) -> None: + if not set(config.MANDATORY_JSON_OBJECTS).issubset(data): + raise SessionValidationError( + "Your JSON file must contain at least these objects: layers, nodes, edges" + ) + layers = data["layers"] + if not layers or any(_empty(row.get("name")) for row in layers): + raise SessionValidationError("JSON layers must each have a non-empty name.") + if len({row["name"] for row in layers}) > config.MAX_LAYERS: + raise SessionValidationError( + f"The network must contain no more than {config.MAX_LAYERS} layers." + ) + nodes = data["nodes"] + if any(_empty(n.get("name")) or _empty(n.get("layer")) for n in nodes): + raise SessionValidationError("JSON nodes must each have a non-empty name and layer.") + edges = data["edges"] + if any(_empty(e.get("src")) or _empty(e.get("trg")) for e in edges): + raise SessionValidationError("JSON edges must each have a non-empty src and trg.") + + +def _handle_channels(edges: list[dict[str, Any]]) -> tuple[list[dict[str, Any]], list[str]]: + """Drop channels entirely if any edge lacks one (ports handleJSONChannels).""" + warnings: list[str] = [] + has_channel = any("channel" in e for e in edges) + if has_channel and any(_empty(e.get("channel")) for e in edges): + for e in edges: + e.pop("channel", None) + if not all(_empty(e.get("channel")) for e in edges): + warnings.append("At least one edge has no channel name. Removing channels completely.") + return edges, warnings + + +def _dedup_edges(edges: list[dict[str, Any]]) -> list[dict[str, Any]]: + seen: set[tuple[str, ...]] = set() + out: list[dict[str, Any]] = [] + has_channel = any("channel" in e for e in edges) + for e in edges: + key = (e["src"], e["trg"], e.get("channel", "")) if has_channel else (e["src"], e["trg"]) + if key not in seen: + seen.add(key) + out.append(e) + return out + + +def normalize_session(data: dict[str, Any]) -> dict[str, Any]: + """Validate + fill defaults, returning the canonical session dict.""" + _validate(data) + + scene = dict(data.get("scene") or {}) + scene["position_x"] = _default(scene.get("position_x")) + scene["position_y"] = _default(scene.get("position_y")) + scene["scale"] = _default(scene.get("scale"), "0.9") + scene["color"] = _default(scene.get("color"), "#000000") + for axis in ("rotation_x", "rotation_y", "rotation_z"): + scene[axis] = _default(scene.get(axis), "0.261799388") + + layers = [dict(row) for row in data["layers"]] + for row in layers: + has_pos = all(not _empty(row.get(f"position_{a}")) for a in "xyz") + row["generate_coordinates"] = not has_pos + for a in "xyz": + row[f"position_{a}"] = _default(row.get(f"position_{a}")) + row[f"rotation_{a}"] = _default(row.get(f"rotation_{a}")) + row["last_layer_scale"] = _default(row.get("last_layer_scale"), "1") + row["floor_current_color"] = _default( + row.get("floor_current_color"), config.FLOOR_DEFAULT_COLOR + ) + row["geometry_parameters_width"] = _default( + row.get("geometry_parameters_width"), config.FLOOR_DEFAULT_WIDTH + ) + + nodes = [dict(n) for n in data["nodes"]] + scramble = all(_empty(n.get("position_x")) for n in nodes) if nodes else True + layer_names = [row["name"] for row in layers] + for n in nodes: + for a in "xyz": + n[f"position_{a}"] = _default(n.get(f"position_{a}")) + n["scale"] = _default(n.get("scale"), 1) + if _empty(n.get("color")): + i = layer_names.index(n["layer"]) if n["layer"] in layer_names else 0 + n["color"] = config.NODE_COLORS[i % len(config.NODE_COLORS)] + n["url"] = _default(n.get("url"), "") + n["descr"] = _default(n.get("descr"), "") + + edges = [dict(e) for e in data["edges"]] + edges, _ = _handle_channels(edges) + edges = _dedup_edges(edges) + for e in edges: + e["opacity"] = _default(e.get("opacity"), 1) + e["color"] = _default(e.get("color"), config.EDGE_DEFAULT_COLOR) + + return { + "scene": scene, + "layers": layers, + "nodes": nodes, + "edges": edges, + "universalLabelColor": _default(data.get("universalLabelColor"), "#FFFFFF"), + "direction": _default(data.get("direction"), False), + "edgeOpacityByWeight": _default(data.get("edgeOpacityByWeight"), True), + "scramble_nodes": scramble, + } diff --git a/backend/tests/test_session.py b/backend/tests/test_session.py new file mode 100644 index 0000000..a04a56a --- /dev/null +++ b/backend/tests/test_session.py @@ -0,0 +1,114 @@ +import json +from pathlib import Path + +import pytest +from fastapi.testclient import TestClient + +from app.main import app +from app.services.session import SessionValidationError, normalize_session + +client = TestClient(app) +DATA = Path(__file__).resolve().parents[2] / "www" / "data" + + +def _minimal(): + return { + "layers": [{"name": "L1"}], + "nodes": [{"name": "A", "layer": "L1"}, {"name": "B", "layer": "L1"}], + "edges": [{"src": "A_L1", "trg": "B_L1"}], + } + + +def test_import_real_api_fixture() -> None: + fixture = DATA / "Arena3DwebApp_aspirin.json" + if not fixture.exists(): + pytest.skip("fixture missing") + resp = client.post( + "/api/session/import", + files={"file": ("net.json", fixture.read_text(), "application/json")}, + ) + assert resp.status_code == 200 + body = resp.json() + assert body["nodes"] and body["edges"] and body["layers"] + + +def test_defaults_filled_for_minimal_json() -> None: + s = normalize_session(_minimal()) + assert s["scene"]["scale"] == "0.9" + assert s["scene"]["color"] == "#000000" + assert s["universalLabelColor"] == "#FFFFFF" + assert s["scramble_nodes"] is True # no node positions given + assert s["nodes"][0]["color"] # palette-assigned + assert s["layers"][0]["generate_coordinates"] is True + + +def test_missing_mandatory_object_raises() -> None: + with pytest.raises(SessionValidationError): + normalize_session({"layers": [{"name": "L1"}], "nodes": []}) + + +def test_empty_layer_name_raises() -> None: + bad = _minimal() + bad["layers"] = [{"name": ""}] + with pytest.raises(SessionValidationError): + normalize_session(bad) + + +def test_channels_dropped_when_partial() -> None: + s = _minimal() + s["edges"] = [ + {"src": "A_L1", "trg": "B_L1", "channel": "x"}, + {"src": "B_L1", "trg": "A_L1", "channel": ""}, + ] + out = normalize_session(s) + assert all("channel" not in e for e in out["edges"]) + + +def test_duplicate_edges_collapsed() -> None: + s = _minimal() + s["edges"] = [{"src": "A_L1", "trg": "B_L1"}, {"src": "A_L1", "trg": "B_L1"}] + assert len(normalize_session(s)["edges"]) == 1 + + +def test_export_is_download() -> None: + resp = client.post("/api/session/export", json=_minimal()) + assert resp.status_code == 200 + assert "attachment" in resp.headers["content-disposition"] + assert resp.json()["layers"][0]["name"] == "L1" + + +def test_external_create_and_resolve(tmp_path, monkeypatch) -> None: + monkeypatch.setattr("app.config.TMP_PATH", str(tmp_path) + "/") + monkeypatch.setattr("app.routers.external.config.TMP_PATH", str(tmp_path) + "/") + created = client.post("/api/external", json=_minimal()) + assert created.status_code == 200 + token = created.json()["token"] + assert token in created.json()["url"] + + resolved = client.get(f"/api/external/{token}") + assert resolved.status_code == 200 + assert resolved.json()["layers"][0]["name"] == "L1" + + +def test_external_missing_token_404(tmp_path, monkeypatch) -> None: + monkeypatch.setattr("app.routers.external.config.TMP_PATH", str(tmp_path) + "/") + assert client.get("/api/external/doesnotexist").status_code == 404 + + +def test_external_rejects_path_traversal(tmp_path, monkeypatch) -> None: + monkeypatch.setattr("app.routers.external.config.TMP_PATH", str(tmp_path) + "/") + resp = client.get("/api/external/..%2f..%2fetc%2fpasswd") + assert resp.status_code in (400, 404) + + +def test_export_fixture_round_trips_through_import(tmp_path) -> None: + fixture = DATA / "figure1_export.json" + if not fixture.exists(): + pytest.skip("fixture missing") + data = json.loads(fixture.read_text()) + # export format uses scene_pan; import treats scene as optional and fills defaults + resp = client.post( + "/api/session/import", + files={"file": ("net.json", json.dumps(data), "application/json")}, + ) + assert resp.status_code == 200 diff --git a/functions/general.R b/functions/general.R deleted file mode 100644 index 14f3377..0000000 --- a/functions/general.R +++ /dev/null @@ -1,32 +0,0 @@ -callJSHandler <- function(handlerName, handlerFunctionParameter) { - session$sendCustomMessage(handlerName, handlerFunctionParameter) -} - -readFromTableFileToDataFrame <- function(path) { - df <- switch( - READ_LIBRARY, - "fread" = data.table::fread(path), - "base" = read.delim(path, header = T) - ) - return(df) -} - -mapper <- function(inArr, min, max, defaultValue = DEFAULT_MAP_VALUE){ - outArr <- inArr - inArr_min <- min(inArr) - inArr_max <- max(inArr) - if (inArr_max - inArr_min != 0) { - for (i in 0:length(inArr)){ - outArr[i] <- (inArr[i] - inArr_min) * (max - min) / (inArr_max - inArr_min) + min; - } - } else outArr[] <- defaultValue; - return(outArr); -} - -extractColumnFrom_node_layerDF <- function(nodeLayerNames, column) { # column = Node or Layer - nodeLayerNames <- as.data.frame(nodeLayerNames) - colnames(nodeLayerNames)[1] <- "NodeLayer" - nodeLayerNames <- plyr::join(nodeLayerNames, node_layerDF, type = "left", - by = "NodeLayer") - return(nodeLayerNames[[column]]) -} diff --git a/functions/init.R b/functions/init.R deleted file mode 100644 index 8676198..0000000 --- a/functions/init.R +++ /dev/null @@ -1,34 +0,0 @@ -initializeServerApp <- function() { - initializeJSVariables() - attachDownloadHandler() - hideDataMetricTabs() -} - -initializeJSVariables <- function() { - callJSHandler("handler_initializeGlobals", - list(MAX_EDGES = MAX_EDGES, MAX_CHANNELS = MAX_CHANNELS, - MAX_LAYERS = MAX_LAYERS, EDGE_DEFAULT_COLOR = EDGE_DEFAULT_COLOR, - CHANNEL_COLORS_DARK = CHANNEL_COLORS_DARK, - CHANNEL_COLORS_LIGHT = CHANNEL_COLORS_LIGHT)) -} - -attachDownloadHandler <- function() { - output$save_network_object <- downloadHandler( - filename = function() { - paste0("network-", Sys.Date(), ".json") - }, content = function(con) { - if (existsNetwork()) { - exportData <- convertSessionToJSON() - write(exportData, con) - } - } - ) -} - -hideDataMetricTabs <- function() { - lapply(c("Network Data", "Selected Edges", "Clustering Data", TOPOLOGY_METRICS), - function(metricName) { - hideTab(inputId = "dataViewPanel", target = metricName) - } - ) -} diff --git a/functions/js_handling.R b/functions/js_handling.R deleted file mode 100644 index 8397438..0000000 --- a/functions/js_handling.R +++ /dev/null @@ -1,24 +0,0 @@ -updateCheckboxInputFromJS <- function(checkboxName, checkboxValue) { - tryCatch({ - updateCheckboxInput(session, checkboxName, - value = (checkboxValue == 'TRUE')) - }, error = function(e) { - print(paste("Checkbox error: ", e)) - renderError("Error with checkbox action.") - }) -} - -toggleCurvatureInputsFromJS <- function() { - tryCatch({ - if (input$js_channel_curvature_flag) { - shinyjs::show("intraChannelCurvature") - shinyjs::show("interChannelCurvature") - } else { - shinyjs::hide("intraChannelCurvature") - shinyjs::hide("interChannelCurvature") - } - }, error = function(e) { - print(paste("Curvature input toggling error: ", e)) - renderError("Error with curvature input toggling.") - }) -} diff --git a/functions/render.R b/functions/render.R deleted file mode 100644 index 7a464d5..0000000 --- a/functions/render.R +++ /dev/null @@ -1,85 +0,0 @@ -renderError <- function(prompt) { - if (exists("session")) - shinyalert::shinyalert("Error!", prompt, type = "error") -} - -renderWarning <- function(prompt) { - if (exists("session")) - shinyalert::shinyalert("Warning!", prompt, type = "warning") -} - -renderModal <- function(prompt) { - showModal(modalDialog(HTML(prompt), footer = NULL)) -} - -renderNetworkDF <- function(formattedNetwork) { - showTab(inputId = "dataViewPanel", target = "Network Data") - updateTabsetPanel(session, "dataViewPanel", selected = "Network Data") - - hiddenColumns <- c(6, 7, 8) - if (is.null(formattedNetwork$Channel)) - hiddenColumns <- hiddenColumns - 1 - renderShinyDataTable("network_dataView", formattedNetwork, fileName = "networkData", - hiddenColumns = hiddenColumns, filter = "top") -} - -renderSelectedEdgesDF <- function(selectedEdgesDF) { - showTab(inputId = "dataViewPanel", target = "Selected Edges") - updateTabsetPanel(session, "dataViewPanel", selected = "Selected Edges") - - hiddenColumns <- c() - if (selectedEdgesDF$Channel[1] == "") - hiddenColumns <- c(4) - renderShinyDataTable("selectedEdges_dataView", selectedEdgesDF, - fileName = "selectedEdgeData", - hiddenColumns = hiddenColumns, filter = "top") -} - -renderClusteringDF <- function(clusteringData) { - showTab(inputId = "dataViewPanel", target = "Clustering Data") - updateTabsetPanel(session, "dataViewPanel", selected = "Clustering Data") - - renderShinyDataTable("clustering_dataView", - clusteringData, - fileName = "clusteringData", - filter = "top") -} - -renderMetricTable <- function(topologyMetricChoice, nodeScale, metric) { - showTab(inputId = "dataViewPanel", target = topologyMetricChoice) - updateTabsetPanel(session, "dataViewPanel", selected = topologyMetricChoice) - renderShinyDataTable(paste0(metric, "_dataView"), - nodeScale, - fileName = paste0(metric, "Data"), - filter = "top") -} - -renderShinyDataTable <- function(shinyOutputId, outputData, - caption = NULL, fileName = "", - scrollY = NULL, hiddenColumns = c(), - filter = "none") { - output[[shinyOutputId]] <- DT::renderDataTable( - outputData, - server = F, - extensions = 'Buttons', - caption = caption, - options = list( - scrollY = scrollY, - scroller = T, - "dom" = 'Blfiprt', - buttons = list( - list(extend = 'excel', filename = fileName), - list(extend = 'csv', filename = fileName), - list(extend = 'copy', filename = fileName), - list(extend = 'pdf', filename = fileName), - list(extend = 'print', filename = fileName) - ), - columnDefs = list( - list(visible = F, targets = hiddenColumns) - ) - ), - filter = filter, - rownames = F, - escape = F - ) -} diff --git a/functions/reset.R b/functions/reset.R deleted file mode 100644 index dfa6b2c..0000000 --- a/functions/reset.R +++ /dev/null @@ -1,44 +0,0 @@ -reset_UI_values <- function() { - hideDataMetricTabs() - # scene - reset("toggleSceneCoords") - reset("autoRotateScene") - # layers - reset("showLayerLabelsRadio") - reset("showLayerCoords") - reset("showWireFrames") - reset("resizeLayerLabels") - reset("layerColorPriorityRadio") - reset("layerOpacity") - # nodes - reset("showNodeLabelsRadio") - reset("resizeNodeLabels") - reset("nodeSelectedColorPriority") - reset("selectAllNodes") - # edges - reset("edgeSelectedColorPriority") - reset("edgeFileColorPriority") - reset("edgeDirectionToggle") - reset("intraLayerEdgeOpacity") - shinyjs::hide("intraLayerEdgeOpacity") - reset("interLayerEdgeOpacity") - shinyjs::hide("interLayerEdgeOpacity") - reset("edgeWidthByWeight") - reset("interDirectionArrowSize") - reset("intraDirectionArrowSize") - shinyjs::hide("interDirectionArrowSize") - shinyjs::hide("intraDirectionArrowSize") - reset("intraChannelCurvature") - shinyjs::hide("intraChannelCurvature") - reset("interChannelCurvature") - shinyjs::hide("interChannelCurvature") - # layouts - reset("selectAllLayersCheckbox") - reset("topologyScaleMetricChoice") - reset("layoutAlgorithmChoice") - reset("clusteringAlgorithmChoice") - reset("localLayoutAlgorithmChoice") - shinyjs::hide("localLayoutAlgorithmChoice") - # other - reset("fps") -} diff --git a/functions/vr.R b/functions/vr.R deleted file mode 100644 index 1f512e4..0000000 --- a/functions/vr.R +++ /dev/null @@ -1,113 +0,0 @@ -handleVRCall <- function() { - tryCatch({ - producePLY(session$token) - produceHTML(session$token) - callJSHandler("handler_browseUrl", paste0(API_URL, session$token)) - }, error = function(e) { - print(paste0("Error in VR parser: ", e)) - renderError("Error while parsing network for VR mode.") - }) -} - -# void function that creates the user-specific VR ply file -# and moves it to the api folder -producePLY <- function(id) { - filename <- paste0('tmp/', id, '.ply') - con <- file(filename) - open(con, "w") - cat(sprintf("ply -format ascii 1.0 -element vertex "), file = con) - # number of nodes - js_nodes <- jsonlite::fromJSON(input$js_nodes_world) - cat(sprintf("%d", nrow(js_nodes)), file = con) - cat(sprintf("\nproperty float x -property float y -property float z -property uint8 red -property uint8 green -property uint8 blue -element edge "), file = con) - # number of edges - js_edge_pairs <- jsonlite::fromJSON(input$js_edge_pairs) - js_edge_colors <- jsonlite::fromJSON(input$js_edge_colors) - edges <- as.data.frame(c(js_edge_pairs, js_edge_colors)) - cat(sprintf("%d", nrow(edges)), file = con) - cat(sprintf("\nproperty int vertex1 -property int vertex2 -property uint8 red -property uint8 green -property uint8 blue -end_header\n"), file = con) - - # node parsing - js_nodes$worldPosition_x <- - js_nodes$worldPosition_x / VR_DOWNSCALE_FACTOR - js_nodes$worldPosition_y <- - js_nodes$worldPosition_y / VR_DOWNSCALE_FACTOR - js_nodes$worldPosition_z <- - js_nodes$worldPosition_z / VR_DOWNSCALE_FACTOR - 5 - - for (i in 1:nrow(js_nodes)){ - rgbColor <- col2rgb(js_nodes$color[i]) - cat(sprintf("%f %f %f %s %s %s\n", - js_nodes$worldPosition_x[i], js_nodes$worldPosition_y[i], - js_nodes$worldPosition_z[i], rgbColor[1], rgbColor[2], - rgbColor[3]), file = con) # r,g,b - } - nodeNames <- paste0(js_nodes$name, "_", js_nodes$layer) - - # edge parsing - for (i in 1:nrow(edges)){ - rgbColor <- col2rgb(edges$color[i]) - nodeIndex1 <- match(edges$src[i], nodeNames) - 1 # starting from index 0 - nodeIndex2 <- match(edges$trg[i], nodeNames) - 1 - cat(sprintf("%s %s %s %s %s\n", - nodeIndex1, nodeIndex2, # from,to - rgbColor[1], rgbColor[2], rgbColor[3]), file = con) # r,g,b - } - close(con) -} - -# void function that creates the user-specific VR html file -# and moves it to the api folder -produceHTML <- function(id) { - js_layers <- jsonlite::fromJSON(input$js_vr_layer_labels) - js_layers$worldPosition_x <- as.numeric(js_layers$worldPosition_x) / VR_DOWNSCALE_FACTOR # pos x - js_layers$worldPosition_y <- as.numeric(js_layers$worldPosition_y) / VR_DOWNSCALE_FACTOR + 1.5 # pos y - js_layers$worldPosition_z <- as.numeric(js_layers$worldPosition_z) / VR_DOWNSCALE_FACTOR - 5 # pos z - - layers_string <- "" - for (i in 1:nrow(js_layers)) { - temp_str <- sprintf('', - js_layers$name[i], js_layers$worldPosition_x[i], - js_layers$worldPosition_y[i], js_layers$worldPosition_z[i]) - layers_string <- paste0(layers_string, temp_str, "\n") - } - - html_string <- paste0(' - - - - - - - - - - - - - - - ', layers_string, ' - - - - - - -') - filename <- paste0('tmp/', id, '.html') - write(html_string, filename) # writing html file to tmp folder -} From ab2ebe81a7a8d510498e58bfb5311ea858c6a808 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Thu, 2 Jul 2026 23:43:59 +0100 Subject: [PATCH 012/166] frontend: EventBus, store, command history, typed API client (Phase 8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Foundation singletons: - bus/index.ts: typed pub/sub, unsubscribe handle (replaces rshiny bridge) - store/index.ts: single AppState source of truth - commands/base.ts: Command + CommandHistory (undo/redo, emits history:changed) - api/client.ts: hand-written typed fetch wrappers mirroring backend Pydantic models (no codegen dependency) — network/layout/topology/ session/external 5 Vitest tests; tsc + eslint clean (skipLibCheck for third-party stubs). Verified live: config/network/layout return 200, OpenAPI exposes all 9 paths. Co-Authored-By: Claude Fable 5 --- PLAN.md | 12 ++-- frontend/src/api/client.ts | 100 ++++++++++++++++++++++++++++++++ frontend/src/bus/index.ts | 41 +++++++++++++ frontend/src/commands/base.ts | 52 +++++++++++++++++ frontend/src/foundation.test.ts | 63 ++++++++++++++++++++ frontend/src/store/index.ts | 33 +++++++++++ frontend/tsconfig.json | 1 + 7 files changed, 296 insertions(+), 6 deletions(-) create mode 100644 frontend/src/api/client.ts create mode 100644 frontend/src/bus/index.ts create mode 100644 frontend/src/commands/base.ts create mode 100644 frontend/src/foundation.test.ts create mode 100644 frontend/src/store/index.ts diff --git a/PLAN.md b/PLAN.md index 4af20d1..88a7390 100644 --- a/PLAN.md +++ b/PLAN.md @@ -99,12 +99,12 @@ Clustering in v2 is not a standalone action — it is an option of the layout ru ## Phase 8 — Frontend: Foundation -- [ ] Install Three.js via npm, configure `@types/three` -- [ ] Implement `EventBus` singleton (`src/bus/index.ts`) -- [ ] Implement `AppState` store singleton (`src/store/index.ts`) -- [ ] Implement `Command` interface + `CommandHistory` singleton (`src/commands/base.ts`) -- [ ] Generate typed API client from FastAPI OpenAPI spec (`src/api/client.ts`) -- [ ] Verify API client can call all Phase 2–7 endpoints +- [x] Install Three.js via npm, configure `@types/three` (already in `package.json`) +- [x] Implement `EventBus` singleton (`src/bus/index.ts`) — typed events, returns unsubscribe fn +- [x] Implement `AppState` store singleton (`src/store/index.ts`) +- [x] Implement `Command` interface + `CommandHistory` singleton (`src/commands/base.ts`) — emits `history:changed` +- [x] Hand-write typed API client mirroring the Pydantic models (`src/api/client.ts`) — no codegen dep; covers network/layout/topology/session/external +- [x] Verify API client can call all Phase 2–7 endpoints (live smoke test: config/network/layout 200; OpenAPI lists all 9 paths). 5 Vitest tests for bus/store/history. --- diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts new file mode 100644 index 0000000..72e2690 --- /dev/null +++ b/frontend/src/api/client.ts @@ -0,0 +1,100 @@ +// Typed REST client — mirrors the FastAPI Pydantic models (backend/app/models). +// Every interaction with the backend is one typed fetch() call (SPEC §3). + +export interface NodeModel { + id: string + label: string + layer: string +} + +export interface EdgeModel { + src: string + trg: string + source_node: string + source_layer: string + target_node: string + target_layer: string + weight: number + scaled_weight: number + channel: string | null +} + +export interface NetworkData { + nodes: NodeModel[] + edges: EdgeModel[] + layers: string[] + channels: string[] + warnings: string[] +} + +export type Scope = 'perLayer' | 'allLayers' | 'nodesPerLayers' + +export interface ClusteringOptions { + algorithm: string + local_layout: string +} + +export interface LayoutRequest { + nodes: NodeModel[] + edges: EdgeModel[] + algorithm: string + scope?: Scope + selected_layers: string[] + selected_nodes?: string[] | null + selected_channels?: string[] | null + clustering?: ClusteringOptions | null + seed?: number +} + +export interface LayoutResponse { + positions: Record + clusters: Record | null +} + +export interface TopologyRequest { + nodes: NodeModel[] + edges: EdgeModel[] + metric: string + scope?: Scope + selected_layers: string[] + selected_nodes?: string[] | null + selected_channels?: string[] | null + directed?: boolean +} + +export interface TopologyResponse { + scales: Record + raw: Record +} + +async function post(path: string, body: unknown): Promise { + const res = await fetch(path, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }) + if (!res.ok) throw new Error(`POST ${path} failed: ${res.status} ${await res.text()}`) + return res.json() as Promise +} + +async function postFile(path: string, file: File | Blob, name = 'file'): Promise { + const form = new FormData() + form.append('file', file, name) + const res = await fetch(path, { method: 'POST', body: form }) + if (!res.ok) throw new Error(`POST ${path} failed: ${res.status} ${await res.text()}`) + return res.json() as Promise +} + +export const api = { + uploadNetwork: (file: File | Blob) => postFile('/api/network', file), + layout: (req: LayoutRequest) => post('/api/layout', req), + topology: (req: TopologyRequest) => post('/api/topology', req), + importSession: (file: File | Blob) => postFile>('/api/session/import', file), + createExternal: (session: unknown) => + post<{ token: string; url: string }>('/api/external', session), + resolveExternal: async (token: string): Promise> => { + const res = await fetch(`/api/external/${token}`) + if (!res.ok) throw new Error(`GET /api/external/${token} failed: ${res.status}`) + return res.json() as Promise> + }, +} diff --git a/frontend/src/bus/index.ts b/frontend/src/bus/index.ts new file mode 100644 index 0000000..61757c5 --- /dev/null +++ b/frontend/src/bus/index.ts @@ -0,0 +1,41 @@ +// Typed event bus — decouples UI panels from the Three.js scene. +// Replaces the role of rshiny_handlers.js / rshiny_update.js. + +export interface BusEvents { + 'network:loaded': { nodeCount: number; edgeCount: number } + 'node:color-changed': { nodeId: string; color: string } + 'node:size-changed': { nodeId: string; size: number } + 'edge:color-changed': { edgeId: string; color: string } + 'layout:applied': { positions: Record } + 'clustering:applied': { clusters: Record } + 'topology:applied': { scales: Record } + 'theme:changed': { theme: string } + 'history:changed': { canUndo: boolean; canRedo: boolean } +} + +type Handler = (payload: T) => void + +class EventBus { + // one Set of handlers per event name; typed via the public method signatures + private handlers = new Map>>() + + on(event: K, handler: Handler): () => void { + let set = this.handlers.get(event) + if (!set) { + set = new Set() + this.handlers.set(event, set) + } + set.add(handler as Handler) + return () => this.off(event, handler) + } + + off(event: K, handler: Handler): void { + this.handlers.get(event)?.delete(handler as Handler) + } + + emit(event: K, payload: BusEvents[K]): void { + this.handlers.get(event)?.forEach((h) => h(payload)) + } +} + +export const bus = new EventBus() diff --git a/frontend/src/commands/base.ts b/frontend/src/commands/base.ts new file mode 100644 index 0000000..696a9c7 --- /dev/null +++ b/frontend/src/commands/base.ts @@ -0,0 +1,52 @@ +// Command pattern + history — undo/redo for all scene mutations (SPEC §4). +import { bus } from '../bus' + +export interface Command { + execute(): void + undo(): void + description: string // shown in UI, e.g. "Apply Force Layout" +} + +class CommandHistory { + private undoStack: Command[] = [] + private redoStack: Command[] = [] + + execute(command: Command): void { + command.execute() + this.undoStack.push(command) + this.redoStack = [] + this.notify() + } + + undo(): void { + const cmd = this.undoStack.pop() + if (cmd) { + cmd.undo() + this.redoStack.push(cmd) + this.notify() + } + } + + redo(): void { + const cmd = this.redoStack.pop() + if (cmd) { + cmd.execute() + this.undoStack.push(cmd) + this.notify() + } + } + + get canUndo(): boolean { + return this.undoStack.length > 0 + } + + get canRedo(): boolean { + return this.redoStack.length > 0 + } + + private notify(): void { + bus.emit('history:changed', { canUndo: this.canUndo, canRedo: this.canRedo }) + } +} + +export const history = new CommandHistory() diff --git a/frontend/src/foundation.test.ts b/frontend/src/foundation.test.ts new file mode 100644 index 0000000..74e4972 --- /dev/null +++ b/frontend/src/foundation.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, it, vi } from 'vitest' +import { bus } from './bus' +import { store } from './store' +import { history, type Command } from './commands/base' + +describe('EventBus', () => { + it('delivers emitted payloads to subscribers', () => { + const seen: string[] = [] + const off = bus.on('node:color-changed', ({ nodeId }) => seen.push(nodeId)) + bus.emit('node:color-changed', { nodeId: 'A', color: '#fff' }) + expect(seen).toEqual(['A']) + off() + bus.emit('node:color-changed', { nodeId: 'B', color: '#000' }) + expect(seen).toEqual(['A']) // unsubscribed + }) +}) + +describe('store', () => { + it('merges partial updates', () => { + store.update({ selectedLayers: ['L1'] }) + expect(store.get().selectedLayers).toEqual(['L1']) + store.update({ selectedNodes: ['A_L1'] }) + expect(store.get().selectedLayers).toEqual(['L1']) // preserved + expect(store.get().selectedNodes).toEqual(['A_L1']) + }) +}) + +describe('CommandHistory', () => { + function counterCommand(log: string[]): Command { + return { + description: 'test', + execute: () => log.push('do'), + undo: () => log.push('undo'), + } + } + + it('executes, undoes and redoes', () => { + const log: string[] = [] + history.execute(counterCommand(log)) + expect(history.canUndo).toBe(true) + history.undo() + expect(log).toEqual(['do', 'undo']) + expect(history.canRedo).toBe(true) + history.redo() + expect(log).toEqual(['do', 'undo', 'do']) + }) + + it('clears redo stack on a new execute', () => { + const log: string[] = [] + history.execute(counterCommand(log)) + history.undo() + history.execute(counterCommand(log)) + expect(history.canRedo).toBe(false) + }) + + it('emits history:changed', () => { + const spy = vi.fn() + const off = bus.on('history:changed', spy) + history.execute({ description: 'x', execute: () => {}, undo: () => {} }) + expect(spy).toHaveBeenCalled() + off() + }) +}) diff --git a/frontend/src/store/index.ts b/frontend/src/store/index.ts new file mode 100644 index 0000000..f0c3e3e --- /dev/null +++ b/frontend/src/store/index.ts @@ -0,0 +1,33 @@ +// Single typed source of truth — replaces scattered Shiny input$ values and JS globals. +import type { AppConfig } from '../api/config' +import type { NetworkData } from '../api/client' + +export interface AppState { + config: AppConfig | null + network: NetworkData | null + selectedLayers: string[] + selectedNodes: string[] + selectedChannels: string[] + currentTheme: string +} + +class Store { + private state: AppState = { + config: null, + network: null, + selectedLayers: [], + selectedNodes: [], + selectedChannels: [], + currentTheme: 'dark', + } + + get(): Readonly { + return this.state + } + + update(patch: Partial): void { + this.state = { ...this.state, ...patch } + } +} + +export const store = new Store() diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index e9d0fc9..eec142c 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -8,6 +8,7 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, "sourceMap": true, + "skipLibCheck": true, "lib": ["ES2022", "DOM", "DOM.Iterable"] }, "include": ["src"] From 4190f0329994fdad2b35f2fb712ed17300764b90 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 04:53:34 +0100 Subject: [PATCH 013/166] frontend: Three.js classes ported to TypeScript (Phase 9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate Scene/Layer/Node/Edge from www/js/classes/ to typed frontend/src/three/ on npm three r170. Key finding: vendored www/js/three/three.js was byte-identical to stock Three.js r117 (diff = only a missing trailing newline). SPEC's "manually patched copy" claim is wrong — no core patches exist, so no src/three/ subclasses were needed. - constants.ts: static geometry + 280-color palette (verbatim from v2) - runtime.ts: typed ctx singleton replacing v2 ambient globals (registries, screen bounds, edge tunables, color-priority flags). nodeGroups/layerGroups were new Map() but only bracket-accessed in v2 -> typed as plain records. - Scene/Layer/Node/Edge.ts: faithful ports; only API change is THREE.Math -> THREE.MathUtils. - three.test.ts: 16 Vitest tests across all four classes. Deferred (ponytail-marked): Scene.rotate() DOM slider + Shiny glue and Layer.getColor() picker DOM read -> Phase 11 canvas_controls. Delete www/js/classes/ + three.js. Keep matrix4.js/drag_controls.js as living spec until Phase 11. tsc + eslint + prettier clean; 21/21 vitest pass. Co-Authored-By: Claude Fable 5 --- MIGRATION.md | 25 +- PLAN.md | 14 +- frontend/src/three/Edge.ts | 295 + frontend/src/three/Layer.ts | 261 + frontend/src/three/Node.ts | 203 + frontend/src/three/Scene.ts | 229 + frontend/src/three/constants.ts | 307 + frontend/src/three/index.ts | 9 + frontend/src/three/runtime.ts | 111 + frontend/src/three/three.test.ts | 175 + www/js/classes/Edge.js | 220 - www/js/classes/Layer.js | 208 - www/js/classes/Node.js | 151 - www/js/classes/Scene.js | 216 - www/js/three/three.js | 51266 ----------------------------- 15 files changed, 1615 insertions(+), 52075 deletions(-) create mode 100644 frontend/src/three/Edge.ts create mode 100644 frontend/src/three/Layer.ts create mode 100644 frontend/src/three/Node.ts create mode 100644 frontend/src/three/Scene.ts create mode 100644 frontend/src/three/constants.ts create mode 100644 frontend/src/three/index.ts create mode 100644 frontend/src/three/runtime.ts create mode 100644 frontend/src/three/three.test.ts delete mode 100644 www/js/classes/Edge.js delete mode 100644 www/js/classes/Layer.js delete mode 100644 www/js/classes/Node.js delete mode 100644 www/js/classes/Scene.js delete mode 100644 www/js/three/three.js diff --git a/MIGRATION.md b/MIGRATION.md index 5cefb3b..9b8f575 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -36,13 +36,24 @@ See `PLAN.md` for the ordered implementation steps. | Old file | New file | Done | |---|---|---| -| `www/js/three/three.js` *(patched)* | `three` npm package + `src/three/` extensions | [ ] | -| `www/js/three/matrix4.js` | `three` npm package (bundled) | [ ] | -| `www/js/three/drag_controls.js` | `three/examples/jsm/controls/DragControls` (npm) | [ ] | -| `www/js/classes/Scene.js` | `frontend/src/classes/Scene.ts` | [ ] | -| `www/js/classes/Layer.js` | `frontend/src/classes/Layer.ts` | [ ] | -| `www/js/classes/Node.js` | `frontend/src/classes/Node.ts` | [ ] | -| `www/js/classes/Edge.js` | `frontend/src/classes/Edge.ts` | [ ] | +| `www/js/three/three.js` *(NOT patched — see below)* | `three` npm package (r170) | [x] | +| `www/js/three/matrix4.js` | `three` npm package (bundled) — kept as Phase 11 spec | [ ] | +| `www/js/three/drag_controls.js` | `three/examples/jsm/controls/DragControls` (npm) — kept as Phase 11 spec | [ ] | +| `www/js/classes/Scene.js` | `frontend/src/three/Scene.ts` | [x] | +| `www/js/classes/Layer.js` | `frontend/src/three/Layer.ts` | [x] | +| `www/js/classes/Node.js` | `frontend/src/three/Node.ts` | [x] | +| `www/js/classes/Edge.js` | `frontend/src/three/Edge.ts` | [x] | + +> **Phase 9 finding:** `www/js/three/three.js` was byte-for-byte identical to +> stock Three.js **r117** (verified by diff — only a missing trailing newline). +> The SPEC's "manually patched copy" claim is wrong: there are **no** core +> patches, so no `src/three/` subclasses were needed. The four classes ported to +> plain TS on npm `three` r170 (only API change: `THREE.Math` → `THREE.MathUtils`). +> Shared ambient globals from `static_variables.js` were replaced by a typed +> `src/three/runtime.ts` context; static geometry/palette constants by +> `src/three/constants.ts`. All four classes now live under `src/three/` +> (not the SPEC's split `classes/` + `three/`), since with no patches one folder +> suffices. `matrix4.js` / `drag_controls.js` stay until Phase 11 (canvas_controls). | `www/js/config/global_variables.js` | `frontend/src/config/global_variables.ts` + `GET /api/config` | [ ] | | `www/js/config/static_variables.js` | `frontend/src/config/static_variables.ts` | [ ] | | `www/js/object_actions/canvas_controls.js` | `frontend/src/actions/canvas_controls.ts` | [ ] | diff --git a/PLAN.md b/PLAN.md index 88a7390..737b9da 100644 --- a/PLAN.md +++ b/PLAN.md @@ -110,13 +110,13 @@ Clustering in v2 is not a standalone action — it is an option of the layout ru ## Phase 9 — Frontend: Three.js Classes -- [ ] Identify all manual patches in `www/js/three/three.js` — document each one -- [ ] Migrate `Scene.js` → `Scene.ts` — reimplement patches as subclasses in `src/three/` -- [ ] Migrate `Layer.js` → `Layer.ts` -- [ ] Migrate `Node.js` → `Node.ts` -- [ ] Migrate `Edge.js` → `Edge.ts` -- [ ] Write Vitest unit tests for all four classes -- [ ] Delete `www/js/classes/`, `www/js/three/` +- [x] Identify all manual patches in `www/js/three/three.js` — **none found**: file is byte-identical to stock Three.js r117 (diff = only a missing trailing newline). SPEC's "patched copy" claim is wrong; no `src/three/` subclasses needed. +- [x] Migrate `Scene.js` → `src/three/Scene.ts` — plain TS on npm `three` r170 (`THREE.Math`→`THREE.MathUtils`). Ambient globals → typed `src/three/runtime.ts` context; statics → `src/three/constants.ts`. `Scene.rotate()` (DOM slider + Shiny glue) deferred to Phase 11. +- [x] Migrate `Layer.js` → `src/three/Layer.ts` +- [x] Migrate `Node.js` → `src/three/Node.ts` +- [x] Migrate `Edge.js` → `src/three/Edge.ts` +- [x] Write Vitest unit tests for all four classes (16 tests in `src/three/three.test.ts`; tsc + eslint + prettier clean) +- [x] Delete `www/js/classes/` + `www/js/three/three.js`. **`matrix4.js` / `drag_controls.js` kept** as living spec for Phase 11 (canvas_controls) — deleted there once ported. --- diff --git a/frontend/src/three/Edge.ts b/frontend/src/three/Edge.ts new file mode 100644 index 0000000..ae88769 --- /dev/null +++ b/frontend/src/three/Edge.ts @@ -0,0 +1,295 @@ +import * as THREE from 'three' +import { SELECTED_DEFAULT_COLOR } from './constants' +import { ctx } from './runtime' + +export interface EdgeOptions { + id?: number + source?: string + target?: string + colors?: string[] + weights?: number[] + channels?: string[] + interLayer?: boolean +} + +export class Edge { + THREE_Object!: THREE.Object3D + sourceNodeIndex = -1 + targetNodeIndex = -1 + sourceLayerIndex = -1 + targetLayerIndex = -1 + + id: number + source: string + target: string + name: string + colors: string[] + importedColors: string[] + weights: number[] + channels: string[] + interLayer: boolean + + isSelected = false + + constructor({ + id = 0, + source = '', + target = '', + colors, + weights = [], + channels = [], + interLayer = false, + }: EdgeOptions) { + this.id = id + this.source = source + this.target = target + this.name = this.source.concat('---').concat(this.target) + this.colors = colors ?? [ctx.edgeDefaultColor] + this.importedColors = this.colors + this.weights = weights + this.channels = channels + this.interLayer = interLayer + + this.initIndexVariables() + this.drawEdge() + } + + initIndexVariables(): void { + this.sourceNodeIndex = ctx.nodeLayerNames.indexOf(this.source) + this.targetNodeIndex = ctx.nodeLayerNames.indexOf(this.target) + this.sourceLayerIndex = + ctx.layerGroups[ctx.nodeGroups[ctx.nodeLayerNames[this.sourceNodeIndex]]] + this.targetLayerIndex = + ctx.layerGroups[ctx.nodeGroups[ctx.nodeLayerNames[this.targetNodeIndex]]] + } + + drawEdge(): void { + const points = this.decidePoints() + + if (this.channels.length === 0) this.createEdge(points) + else this.createChannels(points) + + if (this.interLayer) ctx.scene!.add(this.THREE_Object) + else ctx.layers[this.sourceLayerIndex].addEdge(this.THREE_Object) + } + + decidePoints(): THREE.Vector3[] { + const points: THREE.Vector3[] = [] + if (this.interLayer) + points.push( + ctx.nodeObjects[this.sourceNodeIndex].getWorldPosition(), + ctx.nodeObjects[this.targetNodeIndex].getWorldPosition() + ) + else + points.push( + ctx.nodeObjects[this.sourceNodeIndex].getPosition(), + ctx.nodeObjects[this.targetNodeIndex].getPosition() + ) + return points + } + + createEdge(points: THREE.Vector3[]): void { + const geometry = new THREE.BufferGeometry().setFromPoints(points) + const color = this.decideColor() + const opacity = this.decideOpacity() + const material = new THREE.LineBasicMaterial({ + color: color, + alphaTest: 0.05, + transparent: true, + opacity: opacity, + }) + + this.THREE_Object = new THREE.Line(geometry, material) + + if (ctx.isDirectionEnabled && opacity !== 0) this.createArrow(points, color) + } + + decideColor(i = 0, forExport = false): string { + let color = ctx.edgeDefaultColor + + if (!forExport && this.isSelected && ctx.selectedEdgeColorFlag) + color = SELECTED_DEFAULT_COLOR + else if (this.channels.length > 0 && !ctx.edgeFileColorPriority) + color = ctx.channelColors[this.channels[i]] + else if (ctx.edgeFileColorPriority) color = this.importedColors[i] + + return color + } + + decideOpacity(i = 0): number { + let opacity: number + if (ctx.edgeWidthByWeight) opacity = this.weights[i] + else + opacity = this.interLayer + ? ctx.interLayerEdgeOpacity + : ctx.intraLayerEdgeOpacity + return opacity + } + + createArrow(points: THREE.Vector3[], arrowColor: string): void { + const THREE_arrowHelper = this.createArrowHelper(points, arrowColor) + const THREE_Group = new THREE.Group() + THREE_Group.add(this.THREE_Object) + THREE_Group.add(THREE_arrowHelper) + this.THREE_Object = THREE_Group + } + + createArrowHelper( + points: THREE.Vector3[], + edgeColor: string + ): THREE.ArrowHelper { + const direction = points[1].clone().sub(points[0]) + const origin = points[1] + const length = 1 + let headLength = this.interLayer + ? ctx.interDirectionArrowSize + : ctx.intraDirectionArrowSize + headLength = headLength * 6 + const headWidth = headLength / 4 + + return new THREE.ArrowHelper( + direction.normalize(), + origin, + length, + edgeColor, + headLength, + headWidth + ) + } + + // Channels ====== + createChannels(points: THREE.Vector3[]): void { + let THREE_curveGroup = new THREE.Group() + let pushForce = 0 + let pushForceFlag = false + let direction = 1 + const curveFactor = this.interLayer + ? ctx.interChannelCurvature + : ctx.intraChannelCurvature + + const verticalPushConstant = + (points[0].distanceTo(points[1]) * curveFactor) / 400 + if (this.channels.length % 2 == 0) pushForce = 1 // skip straight line + + for (let i = 0; i < this.channels.length; i++) { + direction = -1 * direction // flipping direction + + if (pushForceFlag) pushForce = pushForce + 1 + pushForceFlag = !pushForceFlag // flipping flag to increase pushForce next round + + const verticalPush = direction * (verticalPushConstant * pushForce) + const color = this.decideColor(i) + const opacity = this.decideOpacity(i) + + THREE_curveGroup = this.createCurve( + THREE_curveGroup, + points[0], + points[1], + verticalPush, + color, + this.channels[i], + opacity + ) + } + + this.THREE_Object = THREE_curveGroup + } + + createCurve( + curveGroup: THREE.Group, + p1: THREE.Vector3, + p2: THREE.Vector3, + verticalPush: number, + color: string, + channelName: string, + opacity: number + ): THREE.Group { + const p3 = p1.clone() + const p4 = p2.clone() + const points = 50 + + p3.addScalar(verticalPush) + p4.addScalar(verticalPush) + + let curve: THREE.CubicBezierCurve3 + if (!this.interLayer) + curve = new THREE.CubicBezierCurve3( + p1, + this.transformMiddlePointOnLayer(p3), + this.transformMiddlePointOnLayer(p4), + p2 + ) + else curve = new THREE.CubicBezierCurve3(p1, p3, p4, p2) + + const curvePoints = curve.getPoints(points) + const curveGeometry = new THREE.BufferGeometry().setFromPoints(curvePoints) + const curveMaterial = new THREE.LineBasicMaterial({ + color: color, + alphaTest: 0.05, + transparent: true, + opacity: opacity, + }) + const curveLine = new THREE.Line(curveGeometry, curveMaterial) + curveLine.userData.tag = channelName + curveLine.visible = ctx.channelVisibility[channelName] + curveGroup.add(curveLine) + + if (ctx.isDirectionEnabled && opacity !== 0) + curveGroup = this.createCurvedArrow( + curveGroup, + curvePoints, + points, + color, + channelName + ) + + return curveGroup + } + + // Helps place arrow lines onto the layer instead of through it. + transformMiddlePointOnLayer(point: THREE.Vector3): THREE.Vector3 { + point.x = 0 + return point + } + + createCurvedArrow( + curveGroup: THREE.Group, + curvePoints: THREE.Vector3[], + points: number, + color: string, + channelName: string + ): THREE.Group { + const arrowHelper = this.createArrowHelper( + [curvePoints[points - 4], curvePoints[points - 1]], + color + ) + arrowHelper.userData.tag = channelName + arrowHelper.visible = ctx.channelVisibility[channelName] + curveGroup.add(arrowHelper) + return curveGroup + } + + // On animate ====== + redrawEdge(): void { + if (this.interLayer) ctx.scene!.remove(this.THREE_Object) + else ctx.layers[this.sourceLayerIndex].removeEdge(this.THREE_Object) + + if (!this.interLayer || (this.interLayer && this.areLayersNotHidden())) + this.drawEdge() + } + + areLayersNotHidden(): boolean { + return ( + ctx.layers[this.sourceLayerIndex].isVisible && + ctx.layers[this.targetLayerIndex].isVisible + ) + } + + select(): void { + this.isSelected = true + } + + deselect(): void { + this.isSelected = false + } +} diff --git a/frontend/src/three/Layer.ts b/frontend/src/three/Layer.ts new file mode 100644 index 0000000..f99785f --- /dev/null +++ b/frontend/src/three/Layer.ts @@ -0,0 +1,261 @@ +import * as THREE from 'three' +import { + PLANE_WIDTHSEGMENTS, + PLANE_HEIGHTSEGMENTS, + LAYER_DEFAULT_COLOR, +} from './constants' +import { ctx } from './runtime' +import type { Axis } from './Node' + +export interface LayerOptions { + id?: number + name?: string + position_x?: number + position_y?: number + position_z?: number + last_layer_scale?: number + rotation_x?: number + rotation_y?: number + rotation_z?: number + floor_current_color?: string + geometry_parameters_width?: number +} + +export class Layer { + plane!: THREE.Mesh + sphere!: THREE.Mesh + + id: number + name: string + last_layer_scale: number + geometry_parameters_width: number + importedColor: string + color: string + + showNodeLabels = false + isSelected = false + isVisible = true + coordSystem: THREE.Line[] = [] + + constructor({ + id = 1, + name = 'Layer1', + position_x = 0, + position_y = 0, + position_z = 0, + last_layer_scale = 1, + rotation_x = 0, + rotation_y = 0, + rotation_z = 0, + floor_current_color = LAYER_DEFAULT_COLOR, + geometry_parameters_width = 2 * ctx.yBoundMax, + }: LayerOptions) { + this.id = id + this.name = name + this.last_layer_scale = last_layer_scale + this.geometry_parameters_width = geometry_parameters_width + this.importedColor = floor_current_color + this.color = floor_current_color + + this.createPlane(geometry_parameters_width, floor_current_color) + this.appendCoordSystem() + this.addLabelSphere(geometry_parameters_width, last_layer_scale) + this.initTranslate(position_x, position_y, position_z) + this.initRotate(rotation_x, rotation_y, rotation_z) + this.initScale(last_layer_scale) + } + + // inits + createPlane(width: number, color: string): void { + const planeGeometry = new THREE.PlaneGeometry( + width, + width, + PLANE_WIDTHSEGMENTS, + PLANE_HEIGHTSEGMENTS + ) + planeGeometry.rotateY(THREE.MathUtils.degToRad(90)) + const planeMaterial = new THREE.MeshBasicMaterial({ + color: color, + alphaTest: 0.05, + wireframe: false, + transparent: true, + opacity: 0.6, + side: THREE.DoubleSide, + }) + this.plane = new THREE.Mesh(planeGeometry, planeMaterial) + } + + appendCoordSystem(): void { + this.coordSystem[0] = this.appendLine(150, 0, 0, '#FB3D2A') // red + this.coordSystem[1] = this.appendLine(0, 150, 0, '#46FB2A') // green + this.coordSystem[2] = this.appendLine(0, 0, 150, '#2AC2FB') // blue + this.toggleCoords(false) + } + + appendLine(x: number, y: number, z: number, color: string): THREE.Line { + const points = [ + this.plane.position, + new THREE.Vector3(x, y, z), + this.plane.position, + new THREE.Vector3(-x, -y, -z), + ] + const geometry = new THREE.BufferGeometry().setFromPoints(points) + const material = new THREE.LineBasicMaterial({ color: color }) + const line = new THREE.Line(geometry, material) + this.plane.add(line) + return line + } + + addLabelSphere(width: number, scale: number): void { + // label will be attached here + const threeSphere = this.createSphere() + this.plane.add(threeSphere) + threeSphere.translateY(-width / 2) + threeSphere.translateZ(width / 2) + threeSphere.position.y = threeSphere.position.y * scale + threeSphere.position.z = threeSphere.position.z * scale + this.sphere = threeSphere + } + + createSphere(): THREE.Mesh { + const geometry = new THREE.SphereGeometry() + const material = new THREE.MeshBasicMaterial({ + color: 'white', + transparent: true, + opacity: 0, + }) + return new THREE.Mesh(geometry, material) + } + + initTranslate(x: number, y: number, z: number): void { + this.setPosition('x', x) + this.setPosition('y', y) + this.setPosition('z', z) + } + + initRotate(x: number, y: number, z: number): void { + this.setRotation('x', x) + this.setRotation('y', y) + this.setRotation('z', z) + } + + initScale(value: number): void { + this.plane.geometry.scale(1, Number(value), Number(value)) + } + + addNode(sphere: THREE.Object3D): void { + this.plane.add(sphere) + } + + addEdge(line: THREE.Object3D): void { + this.plane.add(line) + } + + removeEdge(line: THREE.Object3D): void { + this.plane.remove(line) + } + + // toggle functions + toggleSelection(): void { + this.isSelected = !this.isSelected + } + + toggleVisibility(flag: boolean): void { + this.plane.visible = flag + this.isVisible = flag + } + + toggleCoords(layerCoordsSwitch: boolean): void { + this.coordSystem[0].visible = + this.coordSystem[1].visible = + this.coordSystem[2].visible = + layerCoordsSwitch + } + + toggleWireframe(flag: boolean): void { + ;(this.plane.material as THREE.MeshBasicMaterial).wireframe = flag + } + + // transformations + translateX(x: number): void { + this.plane.translateX(x) + } + translateY(y: number): void { + this.plane.translateY(y) + } + translateZ(z: number): void { + this.plane.translateZ(z) + } + + rotateX(x: number): void { + this.plane.rotateX(x) + } + rotateY(y: number): void { + this.plane.rotateY(y) + } + rotateZ(z: number): void { + this.plane.rotateZ(z) + } + + // setters and getters + getName(): string { + return this.name + } + + setPosition(axis: Axis, value: number): void { + this.plane.position[axis] = value + } + + getPosition(axis: Axis): number { + return this.plane.position[axis] + } + + setRotation(axis: Axis, value: number): void { + this.plane.rotation[axis] = value + } + + getRotation(axis: Axis): number { + return this.plane.rotation[axis] + } + + getWidth(): number { + return (this.plane.geometry as THREE.PlaneGeometry).parameters.width + } + + setScale(value: number | string): void { + const newScaleValue = parseFloat(String(value)) / this.getScale() + this.plane.geometry.scale(1, newScaleValue, newScaleValue) + this.last_layer_scale = parseFloat(String(value)) + } + + getScale(): number { + return this.last_layer_scale + } + + setColor(color: string): void { + this.color = color + ;(this.plane.material as THREE.MeshBasicMaterial).color = new THREE.Color( + color + ) + } + + getColor(): string { + let color = LAYER_DEFAULT_COLOR + if (this.isSelected) { + if (ctx.layerColorPrioritySource === 'default') color = this.importedColor + else if (ctx.layerColorPrioritySource === 'picker') + // ponytail: DOM picker read; the input only exists in the browser UI + // (Phase 13). Guarded so headless/tests fall back to the stored color. + color = + (typeof document !== 'undefined' && + (document.getElementById('floor_color') as HTMLInputElement | null) + ?.value) || + this.color + } else color = this.color + return color + } + + setOpacity(value: number): void { + ;(this.plane.material as THREE.MeshBasicMaterial).opacity = value + } +} diff --git a/frontend/src/three/Node.ts b/frontend/src/three/Node.ts new file mode 100644 index 0000000..72bc7ba --- /dev/null +++ b/frontend/src/three/Node.ts @@ -0,0 +1,203 @@ +import * as THREE from 'three' +import { + SPHERE_RADIUS, + SPHERE_WIDTHSEGMENTS, + SPHERE_HEIGHTSEGMENTS, + BOX_SIZE, + DIAMOND_RADIUS, + CONE_RADIUS, + CONE_HEIGHT, +} from './constants' +import { ctx } from './runtime' + +export type Axis = 'x' | 'y' | 'z' +export type NodeGeometryType = 'sphere' | 'box' | 'diamond' | 'cone' + +export interface NodeOptions { + id?: number + name?: string + layer?: string + nodeLayerName?: string + position_x?: number + position_y?: number + position_z?: number + scale?: number + color?: string + url?: string + descr?: string +} + +export class Node { + sphere!: THREE.Mesh + + id: number + name: string + layer: string + nodeLayerName: string + color: string + importedColor: string + clusterColor: string + url: string + descr: string + + isSelected = false + showLabel = false + cluster = '' + + constructor({ + id = 0, + name = '', + layer = '', + nodeLayerName = '', + position_x = 0, + position_y = 0, + position_z = 0, + scale = 1, + color = '#FFFFFF', + url = '', + descr = '', + }: NodeOptions) { + this.id = id + this.name = name + this.layer = layer + this.nodeLayerName = nodeLayerName + this.color = color + this.importedColor = color + this.clusterColor = color + this.url = url + this.descr = descr + + this.createSphere(color) + this.initTranslate(position_x, position_y, position_z) + this.setScale(scale) + } + + createSphere(nodeColor: string): void { + const geometry = new THREE.SphereGeometry( + SPHERE_RADIUS, + SPHERE_WIDTHSEGMENTS, + SPHERE_HEIGHTSEGMENTS + ) + const material = new THREE.MeshStandardMaterial({ + color: nodeColor, + transparent: true, + }) + this.sphere = new THREE.Mesh(geometry, material) + } + + initTranslate(x: number, y: number, z: number): void { + this.setPosition('x', x) + this.setPosition('y', y) + this.setPosition('z', z) + } + + // transformations + translateX(x: number): void { + this.sphere.translateX(x) + } + translateY(y: number): void { + this.sphere.translateY(y) + } + translateZ(z: number): void { + this.sphere.translateZ(z) + } + + // setters and getters + getName(): string { + return this.name + } + + getLayer(): string { + return this.layer + } + + getNodeLayerName(): string { + return this.nodeLayerName + } + + getPosition(): THREE.Vector3 + getPosition(axis: Axis): number + getPosition(axis?: Axis): THREE.Vector3 | number { + return axis === undefined + ? this.sphere.position + : this.sphere.position[axis] + } + + getWorldPosition(): THREE.Vector3 + getWorldPosition(axis: Axis): number + getWorldPosition(axis?: Axis): THREE.Vector3 | number { + const world = this.sphere.getWorldPosition(new THREE.Vector3()) + return axis === undefined ? world : world[axis] + } + + getOpacity(): number { + return (this.sphere.material as THREE.MeshStandardMaterial).opacity + } + + getColor(): string { + let color = this.color + if (ctx.nodeColorPrioritySource === 'default') color = this.importedColor + else if (ctx.nodeColorPrioritySource === 'cluster') + color = this.clusterColor + return color + } + + getScale(): number { + return this.sphere.scale.x + } + + getCluster(): string { + return this.cluster + } + + setPosition(axis: Axis, value: number): void { + this.sphere.position[axis] = value + } + + setOpacity(value: number): void { + ;(this.sphere.material as THREE.MeshStandardMaterial).opacity = value + } + + setGeometry(type: NodeGeometryType): void { + let geometry: THREE.BufferGeometry + switch (type) { + case 'sphere': + geometry = new THREE.SphereGeometry( + SPHERE_RADIUS, + SPHERE_WIDTHSEGMENTS, + SPHERE_HEIGHTSEGMENTS + ) + break + case 'box': + geometry = new THREE.BoxGeometry(BOX_SIZE, BOX_SIZE, BOX_SIZE) + break + case 'diamond': + geometry = new THREE.OctahedronGeometry(DIAMOND_RADIUS, 0) + break + case 'cone': + geometry = new THREE.ConeGeometry(CONE_RADIUS, CONE_HEIGHT, CONE_RADIUS) + break + default: + console.error(`Unknown geometry type: ${type as string}`) + return + } + this.sphere.geometry.dispose() + this.sphere.geometry = geometry + } + + setColor(hexColor: string, importMode = false, clusterMode = false): void { + ;(this.sphere.material as THREE.MeshStandardMaterial).color = + new THREE.Color(hexColor) + this.color = hexColor + if (importMode) this.importedColor = hexColor + if (clusterMode) this.clusterColor = hexColor + } + + setScale(value: number): void { + this.sphere.scale.x = this.sphere.scale.y = this.sphere.scale.z = value + } + + setCluster(clusterName: string): void { + this.cluster = clusterName + } +} diff --git a/frontend/src/three/Scene.ts b/frontend/src/three/Scene.ts new file mode 100644 index 0000000..ceea4b4 --- /dev/null +++ b/frontend/src/three/Scene.ts @@ -0,0 +1,229 @@ +import * as THREE from 'three' +import { ctx } from './runtime' +import type { Axis } from './Node' + +export class Scene { + THREE_Object!: THREE.Scene + pan: THREE.Mesh | null = null // scene translations applied here + sphere: THREE.Mesh | null = null // scene rotations applied here + coordSystem: THREE.Line[] = [] + defaultColor = '#000000' + autoRotate = false + dragging = false + leftClickPressed = false // drag translating scene + middleClickPressed = false // drag rotating scene + axisPressed = '' // z, x, c + + constructor() { + this.reset() + } + + reset(): void { + this.THREE_Object = new THREE.Scene() + this.pan = null + this.sphere = null + this.coordSystem = ['', '', ''] as unknown as THREE.Line[] + this.defaultColor = '#000000' + this.autoRotate = false + this.dragging = false + this.leftClickPressed = false + this.middleClickPressed = false + this.axisPressed = '' + + this.addLights() + this.addPan() + this.addSphere() + this.appendCoordSystem() + } + + addLights(): void { + this.add(this.createPointLight(1)) + this.add(this.createPointLight(-1)) + this.add(new THREE.AmbientLight(0xffffff)) + } + + createPointLight(orientation: number): THREE.PointLight { + const sphereGeom = new THREE.SphereGeometry() + const lightObject = new THREE.PointLight(0xffffff, 1, 2 * ctx.yBoundMax) + lightObject.add( + new THREE.Mesh( + sphereGeom, + new THREE.MeshBasicMaterial({ color: 0xffffff }) + ) + ) + lightObject.position.set(0, orientation * ctx.yBoundMax, 0) + lightObject.rotateX(THREE.MathUtils.degToRad(90)) + return lightObject + } + + addPan(): void { + const threePan = this.createSphere() + this.add(threePan) + this.pan = threePan + } + + createSphere(): THREE.Mesh { + const geometry = new THREE.SphereGeometry() + const material = new THREE.MeshBasicMaterial({ + color: 'white', + transparent: true, + opacity: 0, + }) + return new THREE.Mesh(geometry, material) + } + + add(threeObject: THREE.Object3D): void { + this.THREE_Object.add(threeObject) + } + + remove(threeObject: THREE.Object3D): void { + this.THREE_Object.remove(threeObject) + } + + addSphere(): void { + const threeSphere = this.createSphere() + this.pan!.add(threeSphere) + this.sphere = threeSphere + } + + appendCoordSystem(): void { + this.coordSystem[0] = this.appendLine(800, 0, 0, '#FB3D2A') // red + this.coordSystem[1] = this.appendLine(0, 800, 0, '#46FB2A') // green + this.coordSystem[2] = this.appendLine(0, 0, 800, '#2AC2FB') // blue + } + + appendLine(x: number, y: number, z: number, color: string): THREE.Line { + const points = [ + this.getPointPosition(), + new THREE.Vector3(x, y, z), + this.getPointPosition(), + new THREE.Vector3(-x, -y, -z), + ] + const geometry = new THREE.BufferGeometry().setFromPoints(points) + const material = new THREE.LineBasicMaterial({ color: color }) + const line = new THREE.Line(geometry, material) + this.sphere!.add(line) + return line + } + + toggleCoords(sceneCoordsSwitch: boolean): void { + this.coordSystem[0].visible = + this.coordSystem[1].visible = + this.coordSystem[2].visible = + sceneCoordsSwitch + } + + exists(): boolean { + return this.pan !== null + } + + tiltDefault(): void { + this.setRotation('x', THREE.MathUtils.degToRad(15)) + this.setRotation('y', THREE.MathUtils.degToRad(15)) + this.setRotation('z', THREE.MathUtils.degToRad(5)) + } + + addLayer(threePlane: THREE.Object3D): void { + this.sphere!.add(threePlane) + } + + translateX(x: number): void { + this.pan!.translateX(x) + } + + translateY(y: number): void { + this.pan!.translateY(y) + } + + translatePanWithArrow(code: number): void { + if (code == 37) this.translateX(-25) // left + if (code == 38) this.translateY(25) // up + if (code == 39) this.translateX(25) // right + if (code == 40) this.translateY(-25) // down + } + + translatePanWithMouse(x: number, y: number): void { + this.translateX(x - ctx.mousePreviousX) + this.translateY(ctx.mousePreviousY - y) + } + + orbitSphereWithMouse(x: number, y: number): void { + const deltaMove = { x: x - ctx.mousePreviousX, y: y - ctx.mousePreviousY } + const deltaRotationQuaternion = new THREE.Quaternion().setFromEuler( + new THREE.Euler( + THREE.MathUtils.degToRad(deltaMove.y), + THREE.MathUtils.degToRad(deltaMove.x), + 0, + 'XYZ' + ) + ) + this.setQuaternion(deltaRotationQuaternion) + } + + setQuaternion(deltaRotationQuaternion: THREE.Quaternion): void { + this.sphere!.quaternion.multiplyQuaternions( + deltaRotationQuaternion, + this.getQuaternion() + ) + } + + getQuaternion(): THREE.Quaternion { + return this.sphere!.quaternion + } + + // ponytail: v2 Scene.rotate() (continuous-rotation button: setInterval + DOM + // slider read + Shiny sync) is UI glue, not scene data model. It lands in + // Phase 11 with canvas_controls, wired to the real slider + EventBus. The + // rotateX/Y/Z primitives it drove are kept here. + + rotateX(x: number): void { + this.sphere!.rotateX(x) + } + + rotateY(y: number): void { + this.sphere!.rotateY(y) + } + + rotateZ(z: number): void { + this.sphere!.rotateZ(z) + } + + recenter(): void { + this.pan!.position.x = this.pan!.position.y = 0 + } + + zoom(deltaY: number): void { + let tempScale = this.getScale() + if (deltaY < 0 && tempScale < 2) tempScale = tempScale * 1.1 + else if (deltaY > 0 && tempScale > 0.2) tempScale = tempScale * 0.9 + this.setScale(tempScale) + } + + getPosition(axis: Axis): number { + return this.pan!.position[axis] + } + + setPosition(axis: Axis, value: number): void { + this.pan!.position[axis] = value + } + + getPointPosition(): THREE.Vector3 { + return this.sphere!.position + } + + getRotation(axis: Axis): number { + return this.sphere!.rotation[axis] + } + + setRotation(axis: Axis, value: number): void { + this.sphere!.rotation[axis] = value + } + + getScale(): number { + return this.pan!.scale.x + } + + setScale(value: number): void { + this.pan!.scale.set(value, value, value) + } +} diff --git a/frontend/src/three/constants.ts b/frontend/src/three/constants.ts new file mode 100644 index 0000000..d23971b --- /dev/null +++ b/frontend/src/three/constants.ts @@ -0,0 +1,307 @@ +// Static geometry + color constants for the Three.js object model. +// Ported from v2 www/js/config/global_variables.js (the `const` block). +// Config-driven / mutable values live in runtime.ts, not here. + +export const NODE_LABEL_DEFAULT_SIZE = '12px' + +// Node geometry +export const SPHERE_RADIUS = 8 +export const SPHERE_WIDTHSEGMENTS = 8 +export const SPHERE_HEIGHTSEGMENTS = 8 +export const BOX_SIZE = 12 +export const DIAMOND_RADIUS = 10 +export const CONE_RADIUS = 8 +export const CONE_HEIGHT = 15 + +// Layer plane geometry +export const PLANE_WIDTHSEGMENTS = 8 +export const PLANE_HEIGHTSEGMENTS = 8 + +// Colors +export const LAYER_DEFAULT_COLOR = '#777777' +export const SELECTED_LAYER_DEFAULT_COLOR = '#f7f43e' +export const SELECTED_DEFAULT_COLOR = '#A3FF00' + +// Categorical node/layer color palette (280 colors). Verbatim from v2. +export const COLOR_VECTOR_280: readonly string[] = [ + '#e41a1c', + '#377eb8', + '#4daf4a', + '#984ea3', + '#ff7f00', + '#ffff33', + '#999999', + '#a65628', + '#f781bf', + '#14a9ad', + '#4ca2f9', + '#a4e43f', + '#d298e2', + '#6119d0', + '#d2737d', + '#c0a43c', + '#f2510e', + '#651be6', + '#79806e', + '#61da5e', + '#cd2f00', + '#9348af', + '#01ac53', + '#c5a4fb', + '#996635', + '#b11573', + '#4bb473', + '#75d89e', + '#2f3f94', + '#2f7b99', + '#da967d', + '#34891f', + '#b0d87b', + '#ca4751', + '#7e50a8', + '#c4d647', + '#e0eeb8', + '#11dec1', + '#289812', + '#566ca0', + '#ffdbe1', + '#2f1179', + '#935b6d', + '#916988', + '#513d98', + '#aead3a', + '#9e6d71', + '#4b5bdc', + '#0cd36d', + '#250662', + '#cb5bea', + '#228916', + '#ac3e1b', + '#df514a', + '#539397', + '#880977', + '#f697c1', + '#ba96ce', + '#679c9d', + '#c6c42c', + '#5d2c52', + '#48b41b', + '#e1cf3b', + '#5be4f0', + '#57c4d8', + '#a4d17a', + '#225b80', + '#be608b', + '#96b00c', + '#088baf', + '#f158bf', + '#e145ba', + '#ee91e3', + '#05d371', + '#5426e0', + '#4834d0', + '#802234', + '#6749e8', + '#0971f0', + '#8fb413', + '#b2b4f0', + '#c3c89d', + '#c9a941', + '#41d158', + '#fb21a3', + '#51aed9', + '#5bb32d', + '#807fb0', + '#21538e', + '#89d534', + '#d36647', + '#7fb411', + '#0023b8', + '#3b8c2a', + '#986b53', + '#f50422', + '#983f7a', + '#ea24a3', + '#79352c', + '#521250', + '#c79ed2', + '#d6dd92', + '#e33e52', + '#b2be57', + '#fa06ec', + '#1bb699', + '#6b2e5f', + '#64820f', + '#1c2710', + '#21538e', + '#89d534', + '#d36647', + '#7fb411', + '#0023b8', + '#3b8c2a', + '#986b53', + '#f50422', + '#983f7a', + '#ea24a3', + '#79352c', + '#521250', + '#c79ed2', + '#d6dd92', + '#e33e52', + '#b2be57', + '#fa06ec', + '#1bb699', + '#6b2e5f', + '#64820f', + '#1c2710', + '#9cb64a', + '#996c48', + '#9ab9b7', + '#06e052', + '#e3a481', + '#0eb621', + '#fc458e', + '#b2db15', + '#aa226d', + '#792ed8', + '#73872a', + '#520d3a', + '#cefcb8', + '#a5b3d9', + '#7d1d85', + '#c4fd57', + '#f1ae16', + '#8fe22a', + '#ef6e3c', + '#243eeb', + '#1dc180', + '#dd93fd', + '#3f8473', + '#e7dbce', + '#421f79', + '#7a3d93', + '#635f6d', + '#93f2d7', + '#9b5c2a', + '#15b9ee', + '#0f5997', + '#409188', + '#911e20', + '#1350ce', + '#10e5b1', + '#fff4d7', + '#cb2582', + '#ce00be', + '#32d5d6', + '#172320', + '#608572', + '#c79bc2', + '#00f87c', + '#77772a', + '#6995ba', + '#fc6b57', + '#f07815', + '#8fd883', + '#060e27', + '#96e591', + '#21d52e', + '#d00043', + '#b47162', + '#1ec227', + '#4f0f6f', + '#1d1d58', + '#947002', + '#bde052', + '#e08c56', + '#28fcfd', + '#bb09b0', + '#36486a', + '#d02e29', + '#1ae6db', + '#3e464c', + '#a84a8f', + '#911e7e', + '#3f16d9', + '#0f525f', + '#ac7c0a', + '#b4c086', + '#c9d730', + '#30cc49', + '#3d6751', + '#fb4c03', + '#640fc1', + '#62c03e', + '#d3493a', + '#88aa0b', + '#406df9', + '#615af0', + '#4be470', + '#2a3434', + '#4a543f', + '#79bca0', + '#a8b8d4', + '#00efd4', + '#7ad236', + '#7260d8', + '#1deaa7', + '#06f43a', + '#823c59', + '#e3d94c', + '#dc1c06', + '#f53b2a', + '#b46238', + '#2dfff6', + '#a82b89', + '#1a8011', + '#436a9f', + '#1a806a', + '#4cf09d', + '#c188a2', + '#67eb4b', + '#b308d3', + '#fc7e41', + '#af3101', + '#ff0650', + '#71b1f4', + '#a2f8a5', + '#e23dd0', + '#d3486d', + '#00f7f9', + '#474893', + '#3cec35', + '#1c65cb', + '#5d1d0c', + '#2d7d2a', + '#ff3420', + '#5cdd87', + '#a259a4', + '#e4ac44', + '#1bede6', + '#8798a4', + '#d7790f', + '#b2c24f', + '#de73c2', + '#d70a9c', + '#25b670', + '#88e9b8', + '#c2b0e2', + '#86e98f', + '#ae90e2', + '#1a806b', + '#436a9e', + '#0ec0ff', + '#f812b3', + '#b17fc9', + '#8d6c2f', + '#d3277a', + '#2ca1ae', + '#9685eb', + '#8a96c6', + '#dba2e6', + '#76fc1b', + '#608fa4', + '#20f6ba', + '#07d7f6', + '#dce77a', + '#77ecca', +] diff --git a/frontend/src/three/index.ts b/frontend/src/three/index.ts new file mode 100644 index 0000000..769e47a --- /dev/null +++ b/frontend/src/three/index.ts @@ -0,0 +1,9 @@ +export * from './constants' +export * from './runtime' +export { Node } from './Node' +export type { NodeOptions, NodeGeometryType, Axis } from './Node' +export { Layer } from './Layer' +export type { LayerOptions } from './Layer' +export { Scene } from './Scene' +export { Edge } from './Edge' +export type { EdgeOptions } from './Edge' diff --git a/frontend/src/three/runtime.ts b/frontend/src/three/runtime.ts new file mode 100644 index 0000000..dae2194 --- /dev/null +++ b/frontend/src/three/runtime.ts @@ -0,0 +1,111 @@ +// Shared mutable runtime state for the Three.js object model. +// Replaces the ambient globals from v2 www/js/config/static_variables.js +// (registries, screen bounds, edge-render tunables, color-priority flags). +// +// The four classes read from this singleton instead of window globals, which +// keeps them unit-testable: a test populates `ctx` before constructing objects. +// Later phases (actions/commands/UI) mutate these fields in response to events. +// +// v2 declared nodeGroups/layerGroups as `new Map()` but only ever used bracket +// notation on them (`layerGroups[name] = i`), i.e. plain dictionaries — typed +// here as records accordingly. + +import type { Scene } from './Scene' +import type { Layer } from './Layer' +import type { Node } from './Node' + +export type ColorPrioritySource = 'default' | 'picker' | 'cluster' + +export interface RuntimeContext { + // Screen bounds (v2: yBoundMax = window.innerHeight / 2) + yBoundMax: number + mousePreviousX: number + mousePreviousY: number + + // Registries — populated when a network loads + scene: Scene | null + layers: Layer[] + nodeObjects: Node[] + nodeLayerNames: string[] + nodeGroups: Record // nodeLayerName -> layerName + layerGroups: Record // layerName -> layer index + + // Channels + channelColors: Record + channelVisibility: Record + + // Edge render config (config-driven; defaults from v2 static_variables.js) + edgeDefaultColor: string + selectedEdgeColorFlag: boolean + edgeFileColorPriority: boolean + isDirectionEnabled: boolean + edgeWidthByWeight: boolean + interLayerEdgeOpacity: number + intraLayerEdgeOpacity: number + interDirectionArrowSize: number + intraDirectionArrowSize: number + interChannelCurvature: number + intraChannelCurvature: number + + // Color-priority sources + layerColorPrioritySource: ColorPrioritySource + nodeColorPrioritySource: ColorPrioritySource +} + +export const ctx: RuntimeContext = { + yBoundMax: typeof window !== 'undefined' ? window.innerHeight / 2 : 400, + mousePreviousX: 0, + mousePreviousY: 0, + + scene: null, + layers: [], + nodeObjects: [], + nodeLayerNames: [], + nodeGroups: {}, + layerGroups: {}, + + channelColors: {}, + channelVisibility: {}, + + edgeDefaultColor: '#000000', + selectedEdgeColorFlag: true, + edgeFileColorPriority: false, + isDirectionEnabled: false, + edgeWidthByWeight: true, + interLayerEdgeOpacity: 0.4, + intraLayerEdgeOpacity: 1, + interDirectionArrowSize: 5, + intraDirectionArrowSize: 5, + interChannelCurvature: 5, + intraChannelCurvature: 15, + + layerColorPrioritySource: 'default', + nodeColorPrioritySource: 'default', +} + +// Reset registries + tunables to defaults (used by tests and network reload). +export function resetContext(): void { + ctx.mousePreviousX = 0 + ctx.mousePreviousY = 0 + ctx.scene = null + ctx.layers = [] + ctx.nodeObjects = [] + ctx.nodeLayerNames = [] + ctx.nodeGroups = {} + ctx.layerGroups = {} + ctx.channelColors = {} + ctx.channelVisibility = {} + ctx.edgeDefaultColor = '#000000' + ctx.selectedEdgeColorFlag = true + ctx.edgeFileColorPriority = false + ctx.isDirectionEnabled = false + ctx.edgeWidthByWeight = true + ctx.interLayerEdgeOpacity = 0.4 + ctx.intraLayerEdgeOpacity = 1 + ctx.interDirectionArrowSize = 5 + ctx.intraDirectionArrowSize = 5 + ctx.interChannelCurvature = 5 + ctx.intraChannelCurvature = 15 + ctx.layerColorPrioritySource = 'default' + ctx.nodeColorPrioritySource = 'default' +} diff --git a/frontend/src/three/three.test.ts b/frontend/src/three/three.test.ts new file mode 100644 index 0000000..f127639 --- /dev/null +++ b/frontend/src/three/three.test.ts @@ -0,0 +1,175 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import * as THREE from 'three' +import { Node, Layer, Scene, Edge, ctx, resetContext } from './index' + +beforeEach(() => { + resetContext() +}) + +describe('Node', () => { + it('constructs a sphere mesh at the given position with defaults', () => { + const n = new Node({ + name: 'A', + position_x: 1, + position_y: 2, + position_z: 3, + }) + expect(n.sphere).toBeInstanceOf(THREE.Mesh) + expect(n.getPosition('x')).toBe(1) + expect(n.getPosition()).toBeInstanceOf(THREE.Vector3) + expect(n.color).toBe('#FFFFFF') + }) + + it('getColor follows the runtime priority source', () => { + const n = new Node({ color: '#111111' }) + n.setColor('#222222', true /* import */, false) + n.setColor('#333333', false, true /* cluster */) + ctx.nodeColorPrioritySource = 'default' + expect(n.getColor()).toBe('#222222') + ctx.nodeColorPrioritySource = 'cluster' + expect(n.getColor()).toBe('#333333') + }) + + it('setScale and getScale round-trip', () => { + const n = new Node({}) + n.setScale(2.5) + expect(n.getScale()).toBe(2.5) + }) + + it('setGeometry swaps the mesh geometry', () => { + const n = new Node({}) + n.setGeometry('box') + expect(n.sphere.geometry).toBeInstanceOf(THREE.BoxGeometry) + n.setGeometry('diamond') + expect(n.sphere.geometry).toBeInstanceOf(THREE.OctahedronGeometry) + }) +}) + +describe('Layer', () => { + it('constructs a plane of the requested width', () => { + const l = new Layer({ name: 'L1', geometry_parameters_width: 400 }) + expect(l.plane).toBeInstanceOf(THREE.Mesh) + expect(l.getWidth()).toBe(400) + expect(l.coordSystem).toHaveLength(3) + }) + + it('setColor updates the material color', () => { + const l = new Layer({ geometry_parameters_width: 100 }) + l.setColor('#ff0000') + expect(l.color).toBe('#ff0000') + const mat = l.plane.material as THREE.MeshBasicMaterial + expect(mat.color.getHexString()).toBe('ff0000') + }) + + it('toggleVisibility syncs plane + flag', () => { + const l = new Layer({ geometry_parameters_width: 100 }) + l.toggleVisibility(false) + expect(l.isVisible).toBe(false) + expect(l.plane.visible).toBe(false) + }) + + it('getColor prefers importedColor when selected in default mode', () => { + const l = new Layer({ + geometry_parameters_width: 100, + floor_current_color: '#abcabc', + }) + l.setColor('#123123') + l.isSelected = true + ctx.layerColorPrioritySource = 'default' + expect(l.getColor()).toBe('#abcabc') // importedColor + l.isSelected = false + expect(l.getColor()).toBe('#123123') // stored color + }) + + it('setScale records last_layer_scale', () => { + const l = new Layer({ geometry_parameters_width: 100 }) + l.setScale(3) + expect(l.getScale()).toBe(3) + }) +}) + +describe('Scene', () => { + it('constructs with pan/sphere and a 3-axis coord system', () => { + const s = new Scene() + expect(s.exists()).toBe(true) + expect(s.THREE_Object).toBeInstanceOf(THREE.Scene) + expect(s.coordSystem).toHaveLength(3) + }) + + it('zoom scales within clamps', () => { + const s = new Scene() + expect(s.getScale()).toBe(1) + s.zoom(-1) // zoom in + expect(s.getScale()).toBeCloseTo(1.1) + s.setScale(0.2) + s.zoom(1) // zoom out below floor -> no change + expect(s.getScale()).toBe(0.2) + }) + + it('rotate primitives rotate the sphere', () => { + const s = new Scene() + s.setRotation('x', 0) + s.rotateX(0.5) + expect(s.getRotation('x')).toBeCloseTo(0.5) + }) +}) + +describe('Edge', () => { + // Minimal populated runtime: one layer, two nodes, both on layer "L". + function seedTwoNodeLayer(): void { + const scene = new Scene() + const layer = new Layer({ geometry_parameters_width: 100 }) + const a = new Node({ name: 'A', position_y: 0, position_z: 0 }) + const b = new Node({ name: 'B', position_y: 10, position_z: 10 }) + ctx.scene = scene + ctx.layers = [layer] + ctx.nodeObjects = [a, b] + ctx.nodeLayerNames = ['A::L', 'B::L'] + ctx.nodeGroups = { 'A::L': 'L', 'B::L': 'L' } + ctx.layerGroups = { L: 0 } + } + + it('draws an intra-layer line and attaches it to the source layer', () => { + seedTwoNodeLayer() + const e = new Edge({ source: 'A::L', target: 'B::L', weights: [1] }) + expect(e.THREE_Object).toBeInstanceOf(THREE.Line) + expect(e.sourceLayerIndex).toBe(0) + expect(ctx.layers[0].plane.children).toContain(e.THREE_Object) + expect(e.decideColor()).toBe(ctx.edgeDefaultColor) + }) + + it('attaches an inter-layer edge to the scene, not a layer', () => { + seedTwoNodeLayer() + const e = new Edge({ + source: 'A::L', + target: 'B::L', + weights: [1], + interLayer: true, + }) + expect(ctx.scene!.THREE_Object.children).toContain(e.THREE_Object) + }) + + it('builds a channel curve group coloured by channelColors', () => { + seedTwoNodeLayer() + ctx.channelColors = { ch1: '#123456' } + ctx.channelVisibility = { ch1: true } + const e = new Edge({ + source: 'A::L', + target: 'B::L', + weights: [0.5], + channels: ['ch1'], + }) + expect(e.THREE_Object).toBeInstanceOf(THREE.Group) + expect(e.THREE_Object.children.length).toBeGreaterThan(0) + expect(e.decideColor(0)).toBe('#123456') + }) + + it('decideOpacity uses weight when edgeWidthByWeight is on', () => { + seedTwoNodeLayer() + const e = new Edge({ source: 'A::L', target: 'B::L', weights: [0.7] }) + ctx.edgeWidthByWeight = true + expect(e.decideOpacity(0)).toBe(0.7) + ctx.edgeWidthByWeight = false + expect(e.decideOpacity(0)).toBe(ctx.intraLayerEdgeOpacity) + }) +}) diff --git a/www/js/classes/Edge.js b/www/js/classes/Edge.js deleted file mode 100644 index 89f4169..0000000 --- a/www/js/classes/Edge.js +++ /dev/null @@ -1,220 +0,0 @@ -class Edge { - constructor({id = 0, source = "", target = "", - colors = [EDGE_DEFAULT_COLOR], weights = [], channels = [], - interLayer = false}) { - this.THREE_Object = ""; - this.sourceNodeIndex = ""; - this.targetNodeIndex = ""; - this.sourceLayerIndex = ""; - this.targetLayerIndex = ""; - - this.id = id; - this.source = source; - this.target = target; - this.name = this.source.concat("---").concat(this.target); - this.colors = colors; - this.importedColors = colors; - this.weights = weights; - this.channels = channels; - this.interLayer = interLayer; - - this.isSelected = false; - - this.initIndexVariables(); - this.drawEdge(); - } - - initIndexVariables() { - this.sourceNodeIndex = nodeLayerNames.indexOf(this.source); - this.targetNodeIndex = nodeLayerNames.indexOf(this.target); - this.sourceLayerIndex = layerGroups[nodeGroups[nodeLayerNames[this.sourceNodeIndex]]]; - this.targetLayerIndex = layerGroups[nodeGroups[nodeLayerNames[this.targetNodeIndex]]]; - } - - drawEdge() { - let points = this.decidePoints(); - - if (this.channels.length === 0) - this.createEdge(points); - else - this.createChannels(points); - - if (this.interLayer) - scene.add(this.THREE_Object); - else - layers[this.sourceLayerIndex].addEdge(this.THREE_Object); - } - - decidePoints() { - let points = []; - - if (this.interLayer) - points.push(nodeObjects[this.sourceNodeIndex].getWorldPosition(), - nodeObjects[this.targetNodeIndex].getWorldPosition()); - else - points.push(nodeObjects[this.sourceNodeIndex].getPosition(), - nodeObjects[this.targetNodeIndex].getPosition()); - - return(points) - } - - createEdge(points) { - let geometry, color, opacity, material; - - geometry = new THREE.BufferGeometry().setFromPoints(points); - color = this.decideColor(); - opacity = this.decideOpacity(); - material = new THREE.LineBasicMaterial({ - color: color, alphaTest: 0.05, transparent: true, opacity: opacity - }); - - this.THREE_Object = new THREE.Line(geometry, material); - - if (isDirectionEnabled && (opacity !== 0)) - this.createArrow(points, color); - } - - decideColor(i = 0, forExport = false) { - let color = EDGE_DEFAULT_COLOR; - - if (!forExport && this.isSelected && selectedEdgeColorFlag) - color = SELECTED_DEFAULT_COLOR; - else if (this.channels.length > 0 && !edgeFileColorPriority) - color = channelColors[this.channels[i]]; - else if (edgeFileColorPriority) - color = this.importedColors[i]; - - return(color) - } - - decideOpacity(i = 0) { - let opacity; - - if (edgeWidthByWeight) - opacity = this.weights[i]; - else - opacity = this.interLayer ? interLayerEdgeOpacity : intraLayerEdgeOpacity; - - return(opacity) - } - - createArrow(points, arrowColor) { - let THREE_arrowHelper, THREE_Group; - THREE_arrowHelper = this.createArrowHelper(points, arrowColor); - THREE_Group = new THREE.Group(); - THREE_Group.add(this.THREE_Object); - THREE_Group.add(THREE_arrowHelper); - this.THREE_Object = THREE_Group; - } - - createArrowHelper(points, edgeColor) { - let direction = points[1].clone().sub(points[0]), - origin = points[1], - length = 1, headLength, headWidth; - - headLength = this.interLayer ? interDirectionArrowSize : intraDirectionArrowSize; - headLength = headLength * 6; - headWidth = headLength / 4; - - return(new THREE.ArrowHelper(direction.normalize(), origin, length, edgeColor, headLength, headWidth)) - } - - // Channels ====== - createChannels(points) { - let THREE_curveGroup = new THREE.Group(), - verticalPushConstant, verticalPush, pushForce = 0, pushForceFlag = false, direction = 1, - curveFactor = this.interLayer ? interChannelCurvature : intraChannelCurvature, - color, opacity; - - verticalPushConstant = points[0].distanceTo(points[1]) * curveFactor / 400; - if (this.channels.length % 2 == 0) // skip straight line - pushForce = 1; - - for (let i = 0; i < this.channels.length; i++) { - direction = -1 * direction; // flipping direction - - if (pushForceFlag) - pushForce = pushForce + 1; - pushForceFlag = !pushForceFlag; // flipping flag to increase pushForce next round - - verticalPush = direction * (verticalPushConstant * pushForce); - color = this.decideColor(i); - opacity = this.decideOpacity(i); - - THREE_curveGroup = this.createCurve(THREE_curveGroup, points[0], points[1], - verticalPush, color, this.channels[i], opacity); - } - - this.THREE_Object = THREE_curveGroup; - } - - createCurve(curveGroup, p1, p2, verticalPush, color, channelName, opacity) { - let p3 = p1.clone(), p4 = p2.clone(), - curve, curvePoints, points = 50, - curveLine, curveGeometry, curveMaterial; - - p3.addScalar(verticalPush); - p4.addScalar(verticalPush); - - if (!this.interLayer) - curve = new THREE.CubicBezierCurve3(p1, this.transformMiddlePointOnLayer(p3), - this.transformMiddlePointOnLayer(p4), p2) - else - curve = new THREE.CubicBezierCurve3(p1, p3, p4, p2) - - curvePoints = curve.getPoints(points); - curveGeometry = new THREE.BufferGeometry().setFromPoints(curvePoints); - curveMaterial = new THREE.LineBasicMaterial({ color: color, alphaTest: 0.05, transparent: true, opacity: opacity }); - curveLine = new THREE.Line(curveGeometry, curveMaterial) - curveLine.userData.tag = channelName; - curveLine.visible = channelVisibility[channelName]; - curveGroup.add(curveLine); - - if (isDirectionEnabled && (opacity !== 0)) - curveGroup = this.createCurvedArrow(curveGroup, curvePoints, points, color, channelName) - - return curveGroup; - } - - // This function helps place arrow lines onto the layer instead of through it - transformMiddlePointOnLayer(point) { - point.x = 0; - return(point) - } - - createCurvedArrow(curveGroup, curvePoints, points, color, channelName) { - let arrowHelper = this.createArrowHelper( - [curvePoints[points - 4], curvePoints[points - 1]], - color - ); - arrowHelper.userData.tag = channelName; - arrowHelper.visible = channelVisibility[channelName]; - curveGroup.add(arrowHelper); - return(curveGroup) - } - - // On animate ====== - redrawEdge() { - if (this.interLayer) - scene.remove(this.THREE_Object); - else - layers[this.sourceLayerIndex].removeEdge(this.THREE_Object); - - if ((!this.interLayer) || - (this.interLayer && this.areLayersNotHidden())) - this.drawEdge(); - } - - areLayersNotHidden() { - return(layers[this.sourceLayerIndex].isVisible && - layers[this.targetLayerIndex].isVisible) - } - - select() { - this.isSelected = true; - } - - deselect() { - this.isSelected = false; - } -} diff --git a/www/js/classes/Layer.js b/www/js/classes/Layer.js deleted file mode 100644 index abbc72f..0000000 --- a/www/js/classes/Layer.js +++ /dev/null @@ -1,208 +0,0 @@ -class Layer { - constructor({id = 1, name = "Layer1", - position_x = 0, position_y = 0, position_z = 0, - last_layer_scale = 1, rotation_x = 0, rotation_y = 0, rotation_z = 0, - floor_current_color = LAYER_DEFAULT_COLOR, geometry_parameters_width = 2 * yBoundMax}) { - this.plane = ""; - this.sphere = ""; - - this.id = id; - this.name = name; - this.last_layer_scale = last_layer_scale; - this.geometry_parameters_width = geometry_parameters_width; - this.importedColor = floor_current_color; - this.color = floor_current_color; - - this.showNodeLabels = false; - this.isSelected = false; - this.isVisible = true; - this.coordSystem = ["", "", ""]; - - this.createPlane(geometry_parameters_width, floor_current_color); - this.appendCoordSystem(); - this.addLabelSphere(geometry_parameters_width, last_layer_scale); - this.initTranslate(position_x, position_y, position_z); - this.initRotate(rotation_x, rotation_y, rotation_z); - this.initScale(last_layer_scale); - } - - // inits - createPlane(width, color) { - let planeGeometry = new THREE.PlaneGeometry(width, width, PLANE_WIDTHSEGMENTS, PLANE_HEIGHTSEGMENTS); - planeGeometry.rotateY(THREE.Math.degToRad(90)); - let planeMaterial = new THREE.MeshBasicMaterial({ - color: color, - alphaTest: 0.05, - wireframe: false, - transparent: true, - opacity: 0.6, - side: THREE.DoubleSide, - }); - this.plane = new THREE.Mesh(planeGeometry, planeMaterial); - } - - appendCoordSystem() { - this.coordSystem[0] = this.appendLine(150, 0, 0, "#FB3D2A") // red - this.coordSystem[1] = this.appendLine(0, 150, 0, "#46FB2A") // green - this.coordSystem[2] = this.appendLine(0, 0, 150, "#2AC2FB") // blue - this.toggleCoords(false); - } - - appendLine(x, y, z, color) { - let points = []; - points.push(this.plane.position, new THREE.Vector3(x, y, z) ); - points.push(this.plane.position, new THREE.Vector3(-x, -y, -z)); - let geometry = new THREE.BufferGeometry().setFromPoints(points); - let material = new THREE.LineBasicMaterial({color: color}); - let line = new THREE.Line(geometry, material); - this.plane.add(line); - return(line) - } - - addLabelSphere(width, scale) { // label will be attched here - let threeSphere = this.createSphere(); - this.plane.add(threeSphere); - threeSphere.translateY(-width / 2); - threeSphere.translateZ(width / 2); - threeSphere.position.y = threeSphere.position.y * scale - threeSphere.position.z = threeSphere.position.z * scale - this.sphere = threeSphere; - } - - createSphere() { - let geometry = new THREE.SphereGeometry(); - let material = new THREE.MeshBasicMaterial({ - color: "white", transparent: true, opacity: 0 - }); - let sphere = new THREE.Mesh(geometry, material); - return(sphere); - } - - initTranslate(x, y, z) { - this.setPosition("x", x); - this.setPosition("y", y); - this.setPosition("z", z); - } - - initRotate(x, y, z) { - this.setRotation("x", x); - this.setRotation("y", y); - this.setRotation("z", z); - } - - initScale(value) { - this.plane.geometry.scale(1, Number(value), Number(value)); - } - - addNode(sphere) { - this.plane.add(sphere); - } - - addEdge(line) { - this.plane.add(line); - } - - removeEdge(line) { - this.plane.remove(line); - } - - // toggle functions - toggleSelection() { - this.isSelected = !this.isSelected; - } - - toggleVisibility(flag) { - this.plane.visible = flag; - this.isVisible = flag; - } - - toggleCoords(layerCoordsSwitch) { - this.coordSystem[0].visible = this.coordSystem[1].visible = - this.coordSystem[2].visible = layerCoordsSwitch; - } - - toggleWireframe(flag) { - this.plane.material.wireframe = flag; - } - - // transformations - translateX(x) { - this.plane.translateX(x); - } - - translateY(y) { - this.plane.translateY(y); - } - - translateZ(z) { - this.plane.translateZ(z); - } - - rotateX(x) { - this.plane.rotateX(x); - } - - rotateY(y) { - this.plane.rotateY(y); - } - - rotateZ(z) { - this.plane.rotateZ(z); - } - - // setters and getters - getName() { - return(this.name); - } - - setPosition(axis, value) { - this.plane.position[axis] = value; - } - - getPosition(axis) { - return(this.plane.position[axis]); - } - - setRotation(axis, value) { - this.plane.rotation[axis] = value; - } - - getRotation(axis) { - return(this.plane.rotation[axis]); - } - - getWidth() { - return(this.plane.geometry.parameters.width); - } - - setScale(value) { - let newScaleValue = parseFloat(value) / this.getScale(); - this.plane.geometry.scale(1, newScaleValue, newScaleValue); - this.last_layer_scale = parseFloat(value); - } - - getScale() { - return(this.last_layer_scale); - } - - setColor(color) { - this.color = color; - this.plane.material.color = new THREE.Color(color); - } - - getColor() { - let color = LAYER_DEFAULT_COLOR; - if (this.isSelected) { - if (layerColorPrioritySource == "default") - color = this.importedColor; - else if (layerColorPrioritySource == "picker") - color = document.getElementById("floor_color").value; - } else - color = this.color; - return(color); - } - - setOpacity(value) { - this.plane.material.opacity = value; - } -} diff --git a/www/js/classes/Node.js b/www/js/classes/Node.js deleted file mode 100644 index 5fc6943..0000000 --- a/www/js/classes/Node.js +++ /dev/null @@ -1,151 +0,0 @@ -class Node { - constructor({id = 0, name = "", layer = "", nodeLayerName = "", - position_x = 0, position_y = 0, position_z = 0, - scale = 1, color = "#FFFFFF", url = "", descr = ""}) { - this.sphere = ""; - - this.id = id; - this.name = name; - this.layer = layer; - this.nodeLayerName = nodeLayerName; - this.color = color; - this.importedColor = color; - this.clusterColor = color; - this.url = url; - this.descr = descr; - - this.isSelected = false; - this.showLabel= false; - this.cluster = ""; - - this.createSphere(color); - this.initTranslate(position_x, position_y, position_z) - this.setScale(scale); - } - - createSphere(nodeColor) { - let geometry = new THREE.SphereGeometry(SPHERE_RADIUS, SPHERE_WIDTHSEGMENTS, SPHERE_HEIGHTSEGMENTS); - let material = new THREE.MeshStandardMaterial({ - color: nodeColor, - transparent: true - }); - this.sphere = new THREE.Mesh(geometry, material); - } - - initTranslate(x, y, z) { - this.setPosition("x", x); - this.setPosition("y", y); - this.setPosition("z", z); - } - - // transformations - translateX(x) { - this.sphere.translateX(x); - } - - translateY(y) { - this.sphere.translateY(y); - } - - translateZ(z) { - this.sphere.translateZ(z); - } - - // setters and getters - getName() { - return(this.name); - } - - getLayer() { - return(this.layer); - } - - getNodeLayerName() { - return(this.nodeLayerName); - } - - getPosition(axis = "") { - if (axis == "") - return(this.sphere.position); - else - return(this.sphere.position[axis]); - } - - getWorldPosition(axis = "") { - if (axis == "") - return(this.sphere.getWorldPosition(new THREE.Vector3())); - else - return(this.sphere.getWorldPosition(new THREE.Vector3())[axis]); - } - - getOpacity() { - return(this.sphere.material.opacity); - } - - getColor() { - let color = this.color; - if (nodeColorPrioritySource == "default") - color = this.importedColor; - else if (nodeColorPrioritySource == "cluster") - color = this.clusterColor; - return(color); - } - - getScale() { - return(this.sphere.scale.x); - } - - getCluster() { - return(this.cluster); - } - - setPosition(axis, value) { - this.sphere.position[axis] = value; - } - - setOpacity(value) { - this.sphere.material.opacity = value; - } - - setGeometry(type) { - let geometry; - switch(type) { - case "sphere": - geometry = new THREE.SphereGeometry(SPHERE_RADIUS, SPHERE_WIDTHSEGMENTS, SPHERE_HEIGHTSEGMENTS); - break; - case "box": - geometry = new THREE.BoxGeometry(BOX_SIZE, BOX_SIZE, BOX_SIZE); - break; - case "diamond": - geometry = new THREE.OctahedronGeometry(DIAMOND_RADIUS, 0); - break; - case "cone": - geometry = new THREE.ConeGeometry(CONE_RADIUS, CONE_HEIGHT, CONE_RADIUS); - break; - default: - console.error(`Unknown geometry type: ${type}`); - return; - } - this.sphere.geometry.dispose(); - this.sphere.geometry = geometry; - } - - setColor(hexColor, importMode = false, clusterMode = false) { - this.sphere.material.color = new THREE.Color(hexColor); - this.color = hexColor; - if (importMode) - this.importedColor = hexColor; - if (clusterMode) - this.clusterColor = hexColor; - } - - setScale(value) { - this.sphere.scale.x = this.sphere.scale.y = - this.sphere.scale.z = value; - } - - setCluster(clusterName) { - this.cluster = clusterName; - } - } - \ No newline at end of file diff --git a/www/js/classes/Scene.js b/www/js/classes/Scene.js deleted file mode 100644 index 3d7eaa2..0000000 --- a/www/js/classes/Scene.js +++ /dev/null @@ -1,216 +0,0 @@ -class Scene { - constructor() { - this.reset(); - } - - reset() { - this.THREE_Object = new THREE.Scene(); - this.pan = ""; - this.sphere = ""; - this.coordSystem = ["", "", ""]; - this.defaultColor = "#000000"; - this.intervalTimeout = ""; - this.autoRotate = false; - this.dragging = false; - this.leftClickPressed = false; // drag translating scene - this.middleClickPressed = false; // drag rotating scene - this.axisPressed = ""; // z, x, c - - this.addLights(); - this.addPan(); - this.addSphere(); - this.appendCoordSystem(); - } - - addLights() { - this.add(this.createPointLight(1)); - this.add(this.createPointLight(-1)); - this.add(new THREE.AmbientLight(0xffffff)); - } - - createPointLight(orientation) { - let sphereGeom = new THREE.SphereGeometry(); - let lightObject = new THREE.PointLight(0xffffff, 1, 2 * yBoundMax); - lightObject.add(new THREE.Mesh(sphereGeom, new THREE.MeshBasicMaterial({color: 0xffffff}))); - lightObject.position.set(0, orientation * yBoundMax, 0); - lightObject.rotateX(THREE.Math.degToRad(90)); - return(lightObject) - } - - addPan() { // scene translations are applied on pan - let threePan = this.createSphere(); - this.add(threePan); - this.pan = threePan; - } - - createSphere() { - let geometry = new THREE.SphereGeometry(); - let material = new THREE.MeshBasicMaterial( - {color: "white", transparent: true, opacity: 0} - ); - let sphere = new THREE.Mesh(geometry, material); - return(sphere); - } - - add(threeObject) { - this.THREE_Object.add(threeObject); - } - - remove(threeObject) { - this.THREE_Object.remove(threeObject); - } - - addSphere() { // scene rotations are applied on sphere - let threeSphere = this.createSphere(); - this.pan.add(threeSphere); - this.sphere = threeSphere; - } - - appendCoordSystem() { - this.coordSystem[0] = this.appendLine(800, 0, 0, "#FB3D2A") // red - this.coordSystem[1] = this.appendLine(0, 800, 0, "#46FB2A") // green - this.coordSystem[2] = this.appendLine(0, 0, 800, "#2AC2FB") // blue - } - - appendLine(x, y, z, color) { - let points = []; - points.push(this.getPointPosition(), new THREE.Vector3(x, y, z) ); - points.push(this.getPointPosition(), new THREE.Vector3(-x, -y, -z)); - let geometry = new THREE.BufferGeometry().setFromPoints(points); - let material = new THREE.LineBasicMaterial({color: color}); - let line = new THREE.Line(geometry, material); - this.sphere.add(line); - return(line) - } - - toggleCoords(sceneCoordsSwitch) { - this.coordSystem[0].visible = this.coordSystem[1].visible = - this.coordSystem[2].visible = sceneCoordsSwitch; - } - - exists() { - return(this.pan !== ""); - } - - tiltDefault() { - this.setRotation("x", THREE.Math.degToRad(15)); - this.setRotation("y", THREE.Math.degToRad(15)); - this.setRotation("z", THREE.Math.degToRad(5)); - } - - addLayer(threePlane) { - this.sphere.add(threePlane); - } - - translateX(x) { - this.pan.translateX(x); - } - - translateY(y) { - this.pan.translateY(y); - } - - translatePanWithArrow(code) { - if (code == 37) this.translateX(-25); //left - if (code == 38) this.translateY(25); //up - if (code == 39) this.translateX(25); //right - if (code == 40) this.translateY(-25); // down - } - - translatePanWithMouse(x, y) { - this.translateX(x - mousePreviousX); - this.translateY(mousePreviousY - y); - } - - orbitSphereWithMouse(x, y) { - let deltaMove = { - x: x - mousePreviousX, - y: y - mousePreviousY - }; - - let deltaRotationQuaternion = new THREE.Quaternion().setFromEuler( - new THREE.Euler(toRadians(deltaMove.y), toRadians(deltaMove.x), 0, 'XYZ')); - - this.setQuaternion(deltaRotationQuaternion); - } - - setQuaternion(deltaRotationQuaternion) { - this.sphere.quaternion.multiplyQuaternions(deltaRotationQuaternion, - this.getQuaternion()); - } - - getQuaternion() { - return(this.sphere.quaternion); - } - - // direction 1 or -1 - rotate(direction, axis) { - clearInterval(this.intervalTimeout); - this.intervalTimeout = setInterval(function() { - let value = document.getElementsByClassName("canvasSlider")[0].value; - value = direction * THREE.Math.degToRad(value); - if (axis == "X") - scene.rotateX(value); - else if (axis == "Y") - scene.rotateY(value); - else if (axis == "Z") - scene.rotateZ(value); - updateSceneSphereRShiny(); - updateVRLayerLabelsRShiny(); - updateVRNodesRShiny(); - }, 70); - } - - rotateX(x) { - this.sphere.rotateX(x); - } - - rotateY(y) { - this.sphere.rotateY(y); - } - - rotateZ(z) { - this.sphere.rotateZ(z); - } - - recenter() { - this.pan.position.x = this.pan.position.y = 0; - } - - zoom(deltaY) { - let tempScale = this.getScale(); - if (deltaY < 0 && tempScale < 2) - tempScale = tempScale * 1.1; - else if (deltaY > 0 && tempScale > 0.2) - tempScale = tempScale * 0.9; - this.setScale(tempScale); - } - - getPosition(axis) { - return(this.pan.position[axis]); - } - - setPosition(axis, value) { - this.pan.position[axis] = value; - } - - getPointPosition() { - return(this.sphere.position); - } - - getRotation(axis) { - return(this.sphere.rotation[axis]); - } - - setRotation(axis, value) { - this.sphere.rotation[axis] = value; - } - - getScale() { - return(this.pan.scale.x); - } - - setScale(value) { - this.pan.scale.set(value, value, value); - } -} diff --git a/www/js/three/three.js b/www/js/three/three.js deleted file mode 100644 index 41d4a6e..0000000 --- a/www/js/three/three.js +++ /dev/null @@ -1,51266 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = global || self, factory(global.THREE = {})); -}(this, (function (exports) { 'use strict'; - - // Polyfills - - if ( Number.EPSILON === undefined ) { - - Number.EPSILON = Math.pow( 2, - 52 ); - - } - - if ( Number.isInteger === undefined ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger - - Number.isInteger = function ( value ) { - - return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value; - - }; - - } - - // - - if ( Math.sign === undefined ) { - - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign - - Math.sign = function ( x ) { - - return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; - - }; - - } - - if ( 'name' in Function.prototype === false ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name - - Object.defineProperty( Function.prototype, 'name', { - - get: function () { - - return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ]; - - } - - } ); - - } - - if ( Object.assign === undefined ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign - - Object.assign = function ( target ) { - - if ( target === undefined || target === null ) { - - throw new TypeError( 'Cannot convert undefined or null to object' ); - - } - - var output = Object( target ); - - for ( var index = 1; index < arguments.length; index ++ ) { - - var source = arguments[ index ]; - - if ( source !== undefined && source !== null ) { - - for ( var nextKey in source ) { - - if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { - - output[ nextKey ] = source[ nextKey ]; - - } - - } - - } - - } - - return output; - - }; - - } - - var REVISION = '117'; - var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; - var TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; - var CullFaceNone = 0; - var CullFaceBack = 1; - var CullFaceFront = 2; - var CullFaceFrontBack = 3; - var FrontFaceDirectionCW = 0; - var FrontFaceDirectionCCW = 1; - var BasicShadowMap = 0; - var PCFShadowMap = 1; - var PCFSoftShadowMap = 2; - var VSMShadowMap = 3; - var FrontSide = 0; - var BackSide = 1; - var DoubleSide = 2; - var FlatShading = 1; - var SmoothShading = 2; - var NoBlending = 0; - var NormalBlending = 1; - var AdditiveBlending = 2; - var SubtractiveBlending = 3; - var MultiplyBlending = 4; - var CustomBlending = 5; - var AddEquation = 100; - var SubtractEquation = 101; - var ReverseSubtractEquation = 102; - var MinEquation = 103; - var MaxEquation = 104; - var ZeroFactor = 200; - var OneFactor = 201; - var SrcColorFactor = 202; - var OneMinusSrcColorFactor = 203; - var SrcAlphaFactor = 204; - var OneMinusSrcAlphaFactor = 205; - var DstAlphaFactor = 206; - var OneMinusDstAlphaFactor = 207; - var DstColorFactor = 208; - var OneMinusDstColorFactor = 209; - var SrcAlphaSaturateFactor = 210; - var NeverDepth = 0; - var AlwaysDepth = 1; - var LessDepth = 2; - var LessEqualDepth = 3; - var EqualDepth = 4; - var GreaterEqualDepth = 5; - var GreaterDepth = 6; - var NotEqualDepth = 7; - var MultiplyOperation = 0; - var MixOperation = 1; - var AddOperation = 2; - var NoToneMapping = 0; - var LinearToneMapping = 1; - var ReinhardToneMapping = 2; - var Uncharted2ToneMapping = 3; - var CineonToneMapping = 4; - var ACESFilmicToneMapping = 5; - - var UVMapping = 300; - var CubeReflectionMapping = 301; - var CubeRefractionMapping = 302; - var EquirectangularReflectionMapping = 303; - var EquirectangularRefractionMapping = 304; - var SphericalReflectionMapping = 305; - var CubeUVReflectionMapping = 306; - var CubeUVRefractionMapping = 307; - var RepeatWrapping = 1000; - var ClampToEdgeWrapping = 1001; - var MirroredRepeatWrapping = 1002; - var NearestFilter = 1003; - var NearestMipmapNearestFilter = 1004; - var NearestMipMapNearestFilter = 1004; - var NearestMipmapLinearFilter = 1005; - var NearestMipMapLinearFilter = 1005; - var LinearFilter = 1006; - var LinearMipmapNearestFilter = 1007; - var LinearMipMapNearestFilter = 1007; - var LinearMipmapLinearFilter = 1008; - var LinearMipMapLinearFilter = 1008; - var UnsignedByteType = 1009; - var ByteType = 1010; - var ShortType = 1011; - var UnsignedShortType = 1012; - var IntType = 1013; - var UnsignedIntType = 1014; - var FloatType = 1015; - var HalfFloatType = 1016; - var UnsignedShort4444Type = 1017; - var UnsignedShort5551Type = 1018; - var UnsignedShort565Type = 1019; - var UnsignedInt248Type = 1020; - var AlphaFormat = 1021; - var RGBFormat = 1022; - var RGBAFormat = 1023; - var LuminanceFormat = 1024; - var LuminanceAlphaFormat = 1025; - var RGBEFormat = RGBAFormat; - var DepthFormat = 1026; - var DepthStencilFormat = 1027; - var RedFormat = 1028; - var RedIntegerFormat = 1029; - var RGFormat = 1030; - var RGIntegerFormat = 1031; - var RGBIntegerFormat = 1032; - var RGBAIntegerFormat = 1033; - - var RGB_S3TC_DXT1_Format = 33776; - var RGBA_S3TC_DXT1_Format = 33777; - var RGBA_S3TC_DXT3_Format = 33778; - var RGBA_S3TC_DXT5_Format = 33779; - var RGB_PVRTC_4BPPV1_Format = 35840; - var RGB_PVRTC_2BPPV1_Format = 35841; - var RGBA_PVRTC_4BPPV1_Format = 35842; - var RGBA_PVRTC_2BPPV1_Format = 35843; - var RGB_ETC1_Format = 36196; - var RGB_ETC2_Format = 37492; - var RGBA_ETC2_EAC_Format = 37496; - var RGBA_ASTC_4x4_Format = 37808; - var RGBA_ASTC_5x4_Format = 37809; - var RGBA_ASTC_5x5_Format = 37810; - var RGBA_ASTC_6x5_Format = 37811; - var RGBA_ASTC_6x6_Format = 37812; - var RGBA_ASTC_8x5_Format = 37813; - var RGBA_ASTC_8x6_Format = 37814; - var RGBA_ASTC_8x8_Format = 37815; - var RGBA_ASTC_10x5_Format = 37816; - var RGBA_ASTC_10x6_Format = 37817; - var RGBA_ASTC_10x8_Format = 37818; - var RGBA_ASTC_10x10_Format = 37819; - var RGBA_ASTC_12x10_Format = 37820; - var RGBA_ASTC_12x12_Format = 37821; - var RGBA_BPTC_Format = 36492; - var SRGB8_ALPHA8_ASTC_4x4_Format = 37840; - var SRGB8_ALPHA8_ASTC_5x4_Format = 37841; - var SRGB8_ALPHA8_ASTC_5x5_Format = 37842; - var SRGB8_ALPHA8_ASTC_6x5_Format = 37843; - var SRGB8_ALPHA8_ASTC_6x6_Format = 37844; - var SRGB8_ALPHA8_ASTC_8x5_Format = 37845; - var SRGB8_ALPHA8_ASTC_8x6_Format = 37846; - var SRGB8_ALPHA8_ASTC_8x8_Format = 37847; - var SRGB8_ALPHA8_ASTC_10x5_Format = 37848; - var SRGB8_ALPHA8_ASTC_10x6_Format = 37849; - var SRGB8_ALPHA8_ASTC_10x8_Format = 37850; - var SRGB8_ALPHA8_ASTC_10x10_Format = 37851; - var SRGB8_ALPHA8_ASTC_12x10_Format = 37852; - var SRGB8_ALPHA8_ASTC_12x12_Format = 37853; - var LoopOnce = 2200; - var LoopRepeat = 2201; - var LoopPingPong = 2202; - var InterpolateDiscrete = 2300; - var InterpolateLinear = 2301; - var InterpolateSmooth = 2302; - var ZeroCurvatureEnding = 2400; - var ZeroSlopeEnding = 2401; - var WrapAroundEnding = 2402; - var NormalAnimationBlendMode = 2500; - var AdditiveAnimationBlendMode = 2501; - var TrianglesDrawMode = 0; - var TriangleStripDrawMode = 1; - var TriangleFanDrawMode = 2; - var LinearEncoding = 3000; - var sRGBEncoding = 3001; - var GammaEncoding = 3007; - var RGBEEncoding = 3002; - var LogLuvEncoding = 3003; - var RGBM7Encoding = 3004; - var RGBM16Encoding = 3005; - var RGBDEncoding = 3006; - var BasicDepthPacking = 3200; - var RGBADepthPacking = 3201; - var TangentSpaceNormalMap = 0; - var ObjectSpaceNormalMap = 1; - - var ZeroStencilOp = 0; - var KeepStencilOp = 7680; - var ReplaceStencilOp = 7681; - var IncrementStencilOp = 7682; - var DecrementStencilOp = 7683; - var IncrementWrapStencilOp = 34055; - var DecrementWrapStencilOp = 34056; - var InvertStencilOp = 5386; - - var NeverStencilFunc = 512; - var LessStencilFunc = 513; - var EqualStencilFunc = 514; - var LessEqualStencilFunc = 515; - var GreaterStencilFunc = 516; - var NotEqualStencilFunc = 517; - var GreaterEqualStencilFunc = 518; - var AlwaysStencilFunc = 519; - - var StaticDrawUsage = 35044; - var DynamicDrawUsage = 35048; - var StreamDrawUsage = 35040; - var StaticReadUsage = 35045; - var DynamicReadUsage = 35049; - var StreamReadUsage = 35041; - var StaticCopyUsage = 35046; - var DynamicCopyUsage = 35050; - var StreamCopyUsage = 35042; - - /** - * https://github.com/mrdoob/eventdispatcher.js/ - */ - - function EventDispatcher() {} - - Object.assign( EventDispatcher.prototype, { - - addEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) { this._listeners = {}; } - - var listeners = this._listeners; - - if ( listeners[ type ] === undefined ) { - - listeners[ type ] = []; - - } - - if ( listeners[ type ].indexOf( listener ) === - 1 ) { - - listeners[ type ].push( listener ); - - } - - }, - - hasEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) { return false; } - - var listeners = this._listeners; - - return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; - - }, - - removeEventListener: function ( type, listener ) { - - if ( this._listeners === undefined ) { return; } - - var listeners = this._listeners; - var listenerArray = listeners[ type ]; - - if ( listenerArray !== undefined ) { - - var index = listenerArray.indexOf( listener ); - - if ( index !== - 1 ) { - - listenerArray.splice( index, 1 ); - - } - - } - - }, - - dispatchEvent: function ( event ) { - - if ( this._listeners === undefined ) { return; } - - var listeners = this._listeners; - var listenerArray = listeners[ event.type ]; - - if ( listenerArray !== undefined ) { - - event.target = this; - - // Make a copy, in case listeners are removed while iterating. - var array = listenerArray.slice( 0 ); - - for ( var i = 0, l = array.length; i < l; i ++ ) { - - array[ i ].call( this, event ); - - } - - } - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - * @author thezwap - */ - - var _lut = []; - - for ( var i = 0; i < 256; i ++ ) { - - _lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ); - - } - - var MathUtils = { - - DEG2RAD: Math.PI / 180, - RAD2DEG: 180 / Math.PI, - - generateUUID: function () { - - // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 - - var d0 = Math.random() * 0xffffffff | 0; - var d1 = Math.random() * 0xffffffff | 0; - var d2 = Math.random() * 0xffffffff | 0; - var d3 = Math.random() * 0xffffffff | 0; - var uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' + - _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' + - _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + - _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; - - // .toUpperCase() here flattens concatenated strings to save heap memory space. - return uuid.toUpperCase(); - - }, - - clamp: function ( value, min, max ) { - - return Math.max( min, Math.min( max, value ) ); - - }, - - // compute euclidian modulo of m % n - // https://en.wikipedia.org/wiki/Modulo_operation - - euclideanModulo: function ( n, m ) { - - return ( ( n % m ) + m ) % m; - - }, - - // Linear mapping from range to range - - mapLinear: function ( x, a1, a2, b1, b2 ) { - - return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); - - }, - - // https://en.wikipedia.org/wiki/Linear_interpolation - - lerp: function ( x, y, t ) { - - return ( 1 - t ) * x + t * y; - - }, - - // http://en.wikipedia.org/wiki/Smoothstep - - smoothstep: function ( x, min, max ) { - - if ( x <= min ) { return 0; } - if ( x >= max ) { return 1; } - - x = ( x - min ) / ( max - min ); - - return x * x * ( 3 - 2 * x ); - - }, - - smootherstep: function ( x, min, max ) { - - if ( x <= min ) { return 0; } - if ( x >= max ) { return 1; } - - x = ( x - min ) / ( max - min ); - - return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); - - }, - - // Random integer from interval - - randInt: function ( low, high ) { - - return low + Math.floor( Math.random() * ( high - low + 1 ) ); - - }, - - // Random float from interval - - randFloat: function ( low, high ) { - - return low + Math.random() * ( high - low ); - - }, - - // Random float from <-range/2, range/2> interval - - randFloatSpread: function ( range ) { - - return range * ( 0.5 - Math.random() ); - - }, - - degToRad: function ( degrees ) { - - return degrees * MathUtils.DEG2RAD; - - }, - - radToDeg: function ( radians ) { - - return radians * MathUtils.RAD2DEG; - - }, - - isPowerOfTwo: function ( value ) { - - return ( value & ( value - 1 ) ) === 0 && value !== 0; - - }, - - ceilPowerOfTwo: function ( value ) { - - return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); - - }, - - floorPowerOfTwo: function ( value ) { - - return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); - - }, - - setQuaternionFromProperEuler: function ( q, a, b, c, order ) { - - // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles - - // rotations are applied to the axes in the order specified by 'order' - // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c' - // angles are in radians - - var cos = Math.cos; - var sin = Math.sin; - - var c2 = cos( b / 2 ); - var s2 = sin( b / 2 ); - - var c13 = cos( ( a + c ) / 2 ); - var s13 = sin( ( a + c ) / 2 ); - - var c1_3 = cos( ( a - c ) / 2 ); - var s1_3 = sin( ( a - c ) / 2 ); - - var c3_1 = cos( ( c - a ) / 2 ); - var s3_1 = sin( ( c - a ) / 2 ); - - switch ( order ) { - - case 'XYX': - q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); - break; - - case 'YZY': - q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); - break; - - case 'ZXZ': - q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); - break; - - case 'XZX': - q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); - break; - - case 'YXY': - q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); - break; - - case 'ZYZ': - q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); - break; - - default: - console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); - - } - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author philogb / http://blog.thejit.org/ - * @author egraether / http://egraether.com/ - * @author zz85 / http://www.lab4games.net/zz85/blog - */ - - function Vector2( x, y ) { - - this.x = x || 0; - this.y = y || 0; - - } - - Object.defineProperties( Vector2.prototype, { - - "width": { - - get: function () { - - return this.x; - - }, - - set: function ( value ) { - - this.x = value; - - } - - }, - - "height": { - - get: function () { - - return this.y; - - }, - - set: function ( value ) { - - this.y = value; - - } - - } - - } ); - - Object.assign( Vector2.prototype, { - - isVector2: true, - - set: function ( x, y ) { - - this.x = x; - this.y = y; - - return this; - - }, - - setScalar: function ( scalar ) { - - this.x = scalar; - this.y = scalar; - - return this; - - }, - - setX: function ( x ) { - - this.x = x; - - return this; - - }, - - setY: function ( y ) { - - this.y = y; - - return this; - - }, - - setComponent: function ( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - }, - - getComponent: function ( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - default: throw new Error( 'index is out of range: ' + index ); - - } - - }, - - clone: function () { - - return new this.constructor( this.x, this.y ); - - }, - - copy: function ( v ) { - - this.x = v.x; - this.y = v.y; - - return this; - - }, - - add: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - - return this; - - }, - - addScalar: function ( s ) { - - this.x += s; - this.y += s; - - return this; - - }, - - addVectors: function ( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - - return this; - - }, - - addScaledVector: function ( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - - return this; - - }, - - sub: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - - return this; - - }, - - subScalar: function ( s ) { - - this.x -= s; - this.y -= s; - - return this; - - }, - - subVectors: function ( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - - return this; - - }, - - multiply: function ( v ) { - - this.x *= v.x; - this.y *= v.y; - - return this; - - }, - - multiplyScalar: function ( scalar ) { - - this.x *= scalar; - this.y *= scalar; - - return this; - - }, - - divide: function ( v ) { - - this.x /= v.x; - this.y /= v.y; - - return this; - - }, - - divideScalar: function ( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - }, - - applyMatrix3: function ( m ) { - - var x = this.x, y = this.y; - var e = m.elements; - - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; - - return this; - - }, - - min: function ( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - - return this; - - }, - - max: function ( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - - return this; - - }, - - clamp: function ( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - - return this; - - }, - - clampScalar: function ( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - - return this; - - }, - - clampLength: function ( min, max ) { - - var length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - }, - - floor: function () { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - - return this; - - }, - - ceil: function () { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - - return this; - - }, - - round: function () { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - - return this; - - }, - - roundToZero: function () { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - - return this; - - }, - - negate: function () { - - this.x = - this.x; - this.y = - this.y; - - return this; - - }, - - dot: function ( v ) { - - return this.x * v.x + this.y * v.y; - - }, - - cross: function ( v ) { - - return this.x * v.y - this.y * v.x; - - }, - - lengthSq: function () { - - return this.x * this.x + this.y * this.y; - - }, - - length: function () { - - return Math.sqrt( this.x * this.x + this.y * this.y ); - - }, - - manhattanLength: function () { - - return Math.abs( this.x ) + Math.abs( this.y ); - - }, - - normalize: function () { - - return this.divideScalar( this.length() || 1 ); - - }, - - angle: function () { - - // computes the angle in radians with respect to the positive x-axis - - var angle = Math.atan2( - this.y, - this.x ) + Math.PI; - - return angle; - - }, - - distanceTo: function ( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - }, - - distanceToSquared: function ( v ) { - - var dx = this.x - v.x, dy = this.y - v.y; - return dx * dx + dy * dy; - - }, - - manhattanDistanceTo: function ( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); - - }, - - setLength: function ( length ) { - - return this.normalize().multiplyScalar( length ); - - }, - - lerp: function ( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - - return this; - - }, - - lerpVectors: function ( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - - return this; - - }, - - equals: function ( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) { offset = 0; } - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) { array = []; } - if ( offset === undefined ) { offset = 0; } - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - - return array; - - }, - - fromBufferAttribute: function ( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - - return this; - - }, - - rotateAround: function ( center, angle ) { - - var c = Math.cos( angle ), s = Math.sin( angle ); - - var x = this.x - center.x; - var y = this.y - center.y; - - this.x = x * c - y * s + center.x; - this.y = x * s + y * c + center.y; - - return this; - - }, - - random: function () { - - this.x = Math.random(); - this.y = Math.random(); - - return this; - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - * @author tschw - */ - - function Matrix3() { - - this.elements = [ - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ]; - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - Object.assign( Matrix3.prototype, { - - isMatrix3: true, - - set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { - - var te = this.elements; - - te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; - te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; - te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; - - return this; - - }, - - identity: function () { - - this.set( - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 - - ); - - return this; - - }, - - clone: function () { - - return new this.constructor().fromArray( this.elements ); - - }, - - copy: function ( m ) { - - var te = this.elements; - var me = m.elements; - - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; - te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; - te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; - - return this; - - }, - - extractBasis: function ( xAxis, yAxis, zAxis ) { - - xAxis.setFromMatrix3Column( this, 0 ); - yAxis.setFromMatrix3Column( this, 1 ); - zAxis.setFromMatrix3Column( this, 2 ); - - return this; - - }, - - setFromMatrix4: function ( m ) { - - var me = m.elements; - - this.set( - - me[ 0 ], me[ 4 ], me[ 8 ], - me[ 1 ], me[ 5 ], me[ 9 ], - me[ 2 ], me[ 6 ], me[ 10 ] - - ); - - return this; - - }, - - multiply: function ( m ) { - - return this.multiplyMatrices( this, m ); - - }, - - premultiply: function ( m ) { - - return this.multiplyMatrices( m, this ); - - }, - - multiplyMatrices: function ( a, b ) { - - var ae = a.elements; - var be = b.elements; - var te = this.elements; - - var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; - var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; - var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; - - var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; - var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; - var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; - - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; - te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; - te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; - - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; - te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; - te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; - - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; - te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; - te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; - - return this; - - }, - - multiplyScalar: function ( s ) { - - var te = this.elements; - - te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; - te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; - te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; - - return this; - - }, - - determinant: function () { - - var te = this.elements; - - var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], - d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], - g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; - - return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; - - }, - - getInverse: function ( matrix, throwOnDegenerate ) { - - if ( throwOnDegenerate !== undefined ) { - - console.warn( "THREE.Matrix3: .getInverse() can no longer be configured to throw on degenerate." ); - - } - - var me = matrix.elements, - te = this.elements, - - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], - n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], - n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], - - t11 = n33 * n22 - n32 * n23, - t12 = n32 * n13 - n33 * n12, - t13 = n23 * n12 - n22 * n13, - - det = n11 * t11 + n21 * t12 + n31 * t13; - - if ( det === 0 ) { return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); } - - var detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; - te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; - - te[ 3 ] = t12 * detInv; - te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; - te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; - - te[ 6 ] = t13 * detInv; - te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; - te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; - - return this; - - }, - - transpose: function () { - - var tmp, m = this.elements; - - tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; - tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; - tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; - - return this; - - }, - - getNormalMatrix: function ( matrix4 ) { - - return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); - - }, - - transposeIntoArray: function ( r ) { - - var m = this.elements; - - r[ 0 ] = m[ 0 ]; - r[ 1 ] = m[ 3 ]; - r[ 2 ] = m[ 6 ]; - r[ 3 ] = m[ 1 ]; - r[ 4 ] = m[ 4 ]; - r[ 5 ] = m[ 7 ]; - r[ 6 ] = m[ 2 ]; - r[ 7 ] = m[ 5 ]; - r[ 8 ] = m[ 8 ]; - - return this; - - }, - - setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) { - - var c = Math.cos( rotation ); - var s = Math.sin( rotation ); - - this.set( - sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, - 0, 0, 1 - ); - - }, - - scale: function ( sx, sy ) { - - var te = this.elements; - - te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; - te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; - - return this; - - }, - - rotate: function ( theta ) { - - var c = Math.cos( theta ); - var s = Math.sin( theta ); - - var te = this.elements; - - var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; - var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; - - te[ 0 ] = c * a11 + s * a21; - te[ 3 ] = c * a12 + s * a22; - te[ 6 ] = c * a13 + s * a23; - - te[ 1 ] = - s * a11 + c * a21; - te[ 4 ] = - s * a12 + c * a22; - te[ 7 ] = - s * a13 + c * a23; - - return this; - - }, - - translate: function ( tx, ty ) { - - var te = this.elements; - - te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; - te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; - - return this; - - }, - - equals: function ( matrix ) { - - var te = this.elements; - var me = matrix.elements; - - for ( var i = 0; i < 9; i ++ ) { - - if ( te[ i ] !== me[ i ] ) { return false; } - - } - - return true; - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) { offset = 0; } - - for ( var i = 0; i < 9; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) { array = []; } - if ( offset === undefined ) { offset = 0; } - - var te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - - array[ offset + 3 ] = te[ 3 ]; - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - array[ offset + 8 ] = te[ 8 ]; - - return array; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - */ - - var _canvas; - - var ImageUtils = { - - getDataURL: function ( image ) { - - var canvas; - - if ( typeof HTMLCanvasElement == 'undefined' ) { - - return image.src; - - } else if ( image instanceof HTMLCanvasElement ) { - - canvas = image; - - } else { - - if ( _canvas === undefined ) { _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); } - - _canvas.width = image.width; - _canvas.height = image.height; - - var context = _canvas.getContext( '2d' ); - - if ( image instanceof ImageData ) { - - context.putImageData( image, 0, 0 ); - - } else { - - context.drawImage( image, 0, 0, image.width, image.height ); - - } - - canvas = _canvas; - - } - - if ( canvas.width > 2048 || canvas.height > 2048 ) { - - return canvas.toDataURL( 'image/jpeg', 0.6 ); - - } else { - - return canvas.toDataURL( 'image/png' ); - - } - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - */ - - var textureId = 0; - - function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - Object.defineProperty( this, 'id', { value: textureId ++ } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - - this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; - this.mipmaps = []; - - this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; - - this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; - this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; - - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - this.minFilter = minFilter !== undefined ? minFilter : LinearMipmapLinearFilter; - - this.anisotropy = anisotropy !== undefined ? anisotropy : 1; - - this.format = format !== undefined ? format : RGBAFormat; - this.internalFormat = null; - this.type = type !== undefined ? type : UnsignedByteType; - - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - this.center = new Vector2( 0, 0 ); - this.rotation = 0; - - this.matrixAutoUpdate = true; - this.matrix = new Matrix3(); - - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - - // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. - // - // Also changing the encoding after already used by a Material will not automatically make the Material - // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. - this.encoding = encoding !== undefined ? encoding : LinearEncoding; - - this.version = 0; - this.onUpdate = null; - - } - - Texture.DEFAULT_IMAGE = undefined; - Texture.DEFAULT_MAPPING = UVMapping; - - Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Texture, - - isTexture: true, - - updateMatrix: function () { - - this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.name = source.name; - - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); - - this.mapping = source.mapping; - - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; - - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; - - this.anisotropy = source.anisotropy; - - this.format = source.format; - this.internalFormat = source.internalFormat; - this.type = source.type; - - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); - this.center.copy( source.center ); - this.rotation = source.rotation; - - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrix.copy( source.matrix ); - - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; - this.encoding = source.encoding; - - return this; - - }, - - toJSON: function ( meta ) { - - var isRootObject = ( meta === undefined || typeof meta === 'string' ); - - if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - - return meta.textures[ this.uuid ]; - - } - - var output = { - - metadata: { - version: 4.5, - type: 'Texture', - generator: 'Texture.toJSON' - }, - - uuid: this.uuid, - name: this.name, - - mapping: this.mapping, - - repeat: [ this.repeat.x, this.repeat.y ], - offset: [ this.offset.x, this.offset.y ], - center: [ this.center.x, this.center.y ], - rotation: this.rotation, - - wrap: [ this.wrapS, this.wrapT ], - - format: this.format, - type: this.type, - encoding: this.encoding, - - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy, - - flipY: this.flipY, - - premultiplyAlpha: this.premultiplyAlpha, - unpackAlignment: this.unpackAlignment - - }; - - if ( this.image !== undefined ) { - - // TODO: Move to THREE.Image - - var image = this.image; - - if ( image.uuid === undefined ) { - - image.uuid = MathUtils.generateUUID(); // UGH - - } - - if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { - - var url; - - if ( Array.isArray( image ) ) { - - // process array of images e.g. CubeTexture - - url = []; - - for ( var i = 0, l = image.length; i < l; i ++ ) { - - url.push( ImageUtils.getDataURL( image[ i ] ) ); - - } - - } else { - - // process single image - - url = ImageUtils.getDataURL( image ); - - } - - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: url - }; - - } - - output.image = image.uuid; - - } - - if ( ! isRootObject ) { - - meta.textures[ this.uuid ] = output; - - } - - return output; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - }, - - transformUv: function ( uv ) { - - if ( this.mapping !== UVMapping ) { return uv; } - - uv.applyMatrix3( this.matrix ); - - if ( uv.x < 0 || uv.x > 1 ) { - - switch ( this.wrapS ) { - - case RepeatWrapping: - - uv.x = uv.x - Math.floor( uv.x ); - break; - - case ClampToEdgeWrapping: - - uv.x = uv.x < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - - uv.x = Math.ceil( uv.x ) - uv.x; - - } else { - - uv.x = uv.x - Math.floor( uv.x ); - - } - - break; - - } - - } - - if ( uv.y < 0 || uv.y > 1 ) { - - switch ( this.wrapT ) { - - case RepeatWrapping: - - uv.y = uv.y - Math.floor( uv.y ); - break; - - case ClampToEdgeWrapping: - - uv.y = uv.y < 0 ? 0 : 1; - break; - - case MirroredRepeatWrapping: - - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - - uv.y = Math.ceil( uv.y ) - uv.y; - - } else { - - uv.y = uv.y - Math.floor( uv.y ); - - } - - break; - - } - - } - - if ( this.flipY ) { - - uv.y = 1 - uv.y; - - } - - return uv; - - } - - } ); - - Object.defineProperty( Texture.prototype, "needsUpdate", { - - set: function ( value ) { - - if ( value === true ) { this.version ++; } - - } - - } ); - - /** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author philogb / http://blog.thejit.org/ - * @author mikael emtinger / http://gomo.se/ - * @author egraether / http://egraether.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - function Vector4( x, y, z, w ) { - - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - this.w = ( w !== undefined ) ? w : 1; - - } - - Object.defineProperties( Vector4.prototype, { - - "width": { - - get: function () { - - return this.z; - - }, - - set: function ( value ) { - - this.z = value; - - } - - }, - - "height": { - - get: function () { - - return this.w; - - }, - - set: function ( value ) { - - this.w = value; - - } - - } - - } ); - - Object.assign( Vector4.prototype, { - - isVector4: true, - - set: function ( x, y, z, w ) { - - this.x = x; - this.y = y; - this.z = z; - this.w = w; - - return this; - - }, - - setScalar: function ( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - this.w = scalar; - - return this; - - }, - - setX: function ( x ) { - - this.x = x; - - return this; - - }, - - setY: function ( y ) { - - this.y = y; - - return this; - - }, - - setZ: function ( z ) { - - this.z = z; - - return this; - - }, - - setW: function ( w ) { - - this.w = w; - - return this; - - }, - - setComponent: function ( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - case 3: this.w = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - }, - - getComponent: function ( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - case 3: return this.w; - default: throw new Error( 'index is out of range: ' + index ); - - } - - }, - - clone: function () { - - return new this.constructor( this.x, this.y, this.z, this.w ); - - }, - - copy: function ( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.w = ( v.w !== undefined ) ? v.w : 1; - - return this; - - }, - - add: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - this.w += v.w; - - return this; - - }, - - addScalar: function ( s ) { - - this.x += s; - this.y += s; - this.z += s; - this.w += s; - - return this; - - }, - - addVectors: function ( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - this.w = a.w + b.w; - - return this; - - }, - - addScaledVector: function ( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - this.w += v.w * s; - - return this; - - }, - - sub: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - this.w -= v.w; - - return this; - - }, - - subScalar: function ( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - this.w -= s; - - return this; - - }, - - subVectors: function ( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - this.w = a.w - b.w; - - return this; - - }, - - multiplyScalar: function ( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; - - return this; - - }, - - applyMatrix4: function ( m ) { - - var x = this.x, y = this.y, z = this.z, w = this.w; - var e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; - this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; - - return this; - - }, - - divideScalar: function ( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - }, - - setAxisAngleFromQuaternion: function ( q ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - - // q is assumed to be normalized - - this.w = 2 * Math.acos( q.w ); - - var s = Math.sqrt( 1 - q.w * q.w ); - - if ( s < 0.0001 ) { - - this.x = 1; - this.y = 0; - this.z = 0; - - } else { - - this.x = q.x / s; - this.y = q.y / s; - this.z = q.z / s; - - } - - return this; - - }, - - setAxisAngleFromRotationMatrix: function ( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - var angle, x, y, z, // variables for result - epsilon = 0.01, // margin to allow for rounding errors - epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees - - te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - if ( ( Math.abs( m12 - m21 ) < epsilon ) && - ( Math.abs( m13 - m31 ) < epsilon ) && - ( Math.abs( m23 - m32 ) < epsilon ) ) { - - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms - - if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && - ( Math.abs( m13 + m31 ) < epsilon2 ) && - ( Math.abs( m23 + m32 ) < epsilon2 ) && - ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { - - // this singularity is identity matrix so angle = 0 - - this.set( 1, 0, 0, 0 ); - - return this; // zero angle, arbitrary axis - - } - - // otherwise this singularity is angle = 180 - - angle = Math.PI; - - var xx = ( m11 + 1 ) / 2; - var yy = ( m22 + 1 ) / 2; - var zz = ( m33 + 1 ) / 2; - var xy = ( m12 + m21 ) / 4; - var xz = ( m13 + m31 ) / 4; - var yz = ( m23 + m32 ) / 4; - - if ( ( xx > yy ) && ( xx > zz ) ) { - - // m11 is the largest diagonal term - - if ( xx < epsilon ) { - - x = 0; - y = 0.707106781; - z = 0.707106781; - - } else { - - x = Math.sqrt( xx ); - y = xy / x; - z = xz / x; - - } - - } else if ( yy > zz ) { - - // m22 is the largest diagonal term - - if ( yy < epsilon ) { - - x = 0.707106781; - y = 0; - z = 0.707106781; - - } else { - - y = Math.sqrt( yy ); - x = xy / y; - z = yz / y; - - } - - } else { - - // m33 is the largest diagonal term so base result on this - - if ( zz < epsilon ) { - - x = 0.707106781; - y = 0.707106781; - z = 0; - - } else { - - z = Math.sqrt( zz ); - x = xz / z; - y = yz / z; - - } - - } - - this.set( x, y, z, angle ); - - return this; // return 180 deg rotation - - } - - // as we have reached here there are no singularities so we can handle normally - - var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + - ( m13 - m31 ) * ( m13 - m31 ) + - ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize - - if ( Math.abs( s ) < 0.001 ) { s = 1; } - - // prevent divide by zero, should not happen if matrix is orthogonal and should be - // caught by singularity test above, but I've left it in just in case - - this.x = ( m32 - m23 ) / s; - this.y = ( m13 - m31 ) / s; - this.z = ( m21 - m12 ) / s; - this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); - - return this; - - }, - - min: function ( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - this.w = Math.min( this.w, v.w ); - - return this; - - }, - - max: function ( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - this.w = Math.max( this.w, v.w ); - - return this; - - }, - - clamp: function ( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - this.w = Math.max( min.w, Math.min( max.w, this.w ) ); - - return this; - - }, - - clampScalar: function ( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); - - return this; - - }, - - clampLength: function ( min, max ) { - - var length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - }, - - floor: function () { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - this.w = Math.floor( this.w ); - - return this; - - }, - - ceil: function () { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - this.w = Math.ceil( this.w ); - - return this; - - }, - - round: function () { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - this.w = Math.round( this.w ); - - return this; - - }, - - roundToZero: function () { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); - - return this; - - }, - - negate: function () { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - this.w = - this.w; - - return this; - - }, - - dot: function ( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; - - }, - - lengthSq: function () { - - return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; - - }, - - length: function () { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); - - }, - - manhattanLength: function () { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); - - }, - - normalize: function () { - - return this.divideScalar( this.length() || 1 ); - - }, - - setLength: function ( length ) { - - return this.normalize().multiplyScalar( length ); - - }, - - lerp: function ( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - this.w += ( v.w - this.w ) * alpha; - - return this; - - }, - - lerpVectors: function ( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - this.w = v1.w + ( v2.w - v1.w ) * alpha; - - return this; - - }, - - equals: function ( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) { offset = 0; } - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - this.w = array[ offset + 3 ]; - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) { array = []; } - if ( offset === undefined ) { offset = 0; } - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - array[ offset + 3 ] = this.w; - - return array; - - }, - - fromBufferAttribute: function ( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - this.w = attribute.getW( index ); - - return this; - - }, - - random: function () { - - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - this.w = Math.random(); - - return this; - - } - - } ); - - /** - * @author szimek / https://github.com/szimek/ - * @author alteredq / http://alteredqualia.com/ - * @author Marius Kintel / https://github.com/kintel - */ - - /* - In options, we can specify: - * Texture parameters for an auto-generated target texture - * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers - */ - function WebGLRenderTarget( width, height, options ) { - - this.width = width; - this.height = height; - - this.scissor = new Vector4( 0, 0, width, height ); - this.scissorTest = false; - - this.viewport = new Vector4( 0, 0, width, height ); - - options = options || {}; - - this.texture = new Texture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); - - this.texture.image = {}; - this.texture.image.width = width; - this.texture.image.height = height; - - this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; - this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; - this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; - - } - - WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: WebGLRenderTarget, - - isWebGLRenderTarget: true, - - setSize: function ( width, height ) { - - if ( this.width !== width || this.height !== height ) { - - this.width = width; - this.height = height; - - this.texture.image.width = width; - this.texture.image.height = height; - - this.dispose(); - - } - - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.width = source.width; - this.height = source.height; - - this.viewport.copy( source.viewport ); - - this.texture = source.texture.clone(); - - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - /** - * @author Mugen87 / https://github.com/Mugen87 - * @author Matt DesLauriers / @mattdesl - */ - - function WebGLMultisampleRenderTarget( width, height, options ) { - - WebGLRenderTarget.call( this, width, height, options ); - - this.samples = 4; - - } - - WebGLMultisampleRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), { - - constructor: WebGLMultisampleRenderTarget, - - isWebGLMultisampleRenderTarget: true, - - copy: function ( source ) { - - WebGLRenderTarget.prototype.copy.call( this, source ); - - this.samples = source.samples; - - return this; - - } - - } ); - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - */ - - function Quaternion( x, y, z, w ) { - - this._x = x || 0; - this._y = y || 0; - this._z = z || 0; - this._w = ( w !== undefined ) ? w : 1; - - } - - Object.assign( Quaternion, { - - slerp: function ( qa, qb, qm, t ) { - - return qm.copy( qa ).slerp( qb, t ); - - }, - - slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - - // fuzz-free, array-based Quaternion SLERP operation - - var x0 = src0[ srcOffset0 + 0 ], - y0 = src0[ srcOffset0 + 1 ], - z0 = src0[ srcOffset0 + 2 ], - w0 = src0[ srcOffset0 + 3 ], - - x1 = src1[ srcOffset1 + 0 ], - y1 = src1[ srcOffset1 + 1 ], - z1 = src1[ srcOffset1 + 2 ], - w1 = src1[ srcOffset1 + 3 ]; - - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - - var s = 1 - t, - - cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, - - dir = ( cos >= 0 ? 1 : - 1 ), - sqrSin = 1 - cos * cos; - - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { - - var sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); - - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; - - } - - var tDir = t * dir; - - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; - - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { - - var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); - - x0 *= f; - y0 *= f; - z0 *= f; - w0 *= f; - - } - - } - - dst[ dstOffset ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; - - }, - - multiplyQuaternionsFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { - - var x0 = src0[ srcOffset0 ]; - var y0 = src0[ srcOffset0 + 1 ]; - var z0 = src0[ srcOffset0 + 2 ]; - var w0 = src0[ srcOffset0 + 3 ]; - - var x1 = src1[ srcOffset1 ]; - var y1 = src1[ srcOffset1 + 1 ]; - var z1 = src1[ srcOffset1 + 2 ]; - var w1 = src1[ srcOffset1 + 3 ]; - - dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; - dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; - dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; - dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; - - return dst; - - } - - } ); - - Object.defineProperties( Quaternion.prototype, { - - x: { - - get: function () { - - return this._x; - - }, - - set: function ( value ) { - - this._x = value; - this._onChangeCallback(); - - } - - }, - - y: { - - get: function () { - - return this._y; - - }, - - set: function ( value ) { - - this._y = value; - this._onChangeCallback(); - - } - - }, - - z: { - - get: function () { - - return this._z; - - }, - - set: function ( value ) { - - this._z = value; - this._onChangeCallback(); - - } - - }, - - w: { - - get: function () { - - return this._w; - - }, - - set: function ( value ) { - - this._w = value; - this._onChangeCallback(); - - } - - } - - } ); - - Object.assign( Quaternion.prototype, { - - isQuaternion: true, - - set: function ( x, y, z, w ) { - - this._x = x; - this._y = y; - this._z = z; - this._w = w; - - this._onChangeCallback(); - - return this; - - }, - - clone: function () { - - return new this.constructor( this._x, this._y, this._z, this._w ); - - }, - - copy: function ( quaternion ) { - - this._x = quaternion.x; - this._y = quaternion.y; - this._z = quaternion.z; - this._w = quaternion.w; - - this._onChangeCallback(); - - return this; - - }, - - setFromEuler: function ( euler, update ) { - - if ( ! ( euler && euler.isEuler ) ) { - - throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - var x = euler._x, y = euler._y, z = euler._z, order = euler.order; - - // http://www.mathworks.com/matlabcentral/fileexchange/ - // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ - // content/SpinCalc.m - - var cos = Math.cos; - var sin = Math.sin; - - var c1 = cos( x / 2 ); - var c2 = cos( y / 2 ); - var c3 = cos( z / 2 ); - - var s1 = sin( x / 2 ); - var s2 = sin( y / 2 ); - var s3 = sin( z / 2 ); - - switch ( order ) { - - case 'XYZ': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'YXZ': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - case 'ZXY': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'ZYX': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - case 'YZX': - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; - break; - - case 'XZY': - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; - break; - - default: - console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); - - } - - if ( update !== false ) { this._onChangeCallback(); } - - return this; - - }, - - setFromAxisAngle: function ( axis, angle ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - - // assumes axis is normalized - - var halfAngle = angle / 2, s = Math.sin( halfAngle ); - - this._x = axis.x * s; - this._y = axis.y * s; - this._z = axis.z * s; - this._w = Math.cos( halfAngle ); - - this._onChangeCallback(); - - return this; - - }, - - setFromRotationMatrix: function ( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - var te = m.elements, - - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], - - trace = m11 + m22 + m33, - s; - - if ( trace > 0 ) { - - s = 0.5 / Math.sqrt( trace + 1.0 ); - - this._w = 0.25 / s; - this._x = ( m32 - m23 ) * s; - this._y = ( m13 - m31 ) * s; - this._z = ( m21 - m12 ) * s; - - } else if ( m11 > m22 && m11 > m33 ) { - - s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - - this._w = ( m32 - m23 ) / s; - this._x = 0.25 * s; - this._y = ( m12 + m21 ) / s; - this._z = ( m13 + m31 ) / s; - - } else if ( m22 > m33 ) { - - s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - - this._w = ( m13 - m31 ) / s; - this._x = ( m12 + m21 ) / s; - this._y = 0.25 * s; - this._z = ( m23 + m32 ) / s; - - } else { - - s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - - this._w = ( m21 - m12 ) / s; - this._x = ( m13 + m31 ) / s; - this._y = ( m23 + m32 ) / s; - this._z = 0.25 * s; - - } - - this._onChangeCallback(); - - return this; - - }, - - setFromUnitVectors: function ( vFrom, vTo ) { - - // assumes direction vectors vFrom and vTo are normalized - - var EPS = 0.000001; - - var r = vFrom.dot( vTo ) + 1; - - if ( r < EPS ) { - - r = 0; - - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - - this._x = - vFrom.y; - this._y = vFrom.x; - this._z = 0; - this._w = r; - - } else { - - this._x = 0; - this._y = - vFrom.z; - this._z = vFrom.y; - this._w = r; - - } - - } else { - - // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 - - this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; - this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; - this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; - this._w = r; - - } - - return this.normalize(); - - }, - - angleTo: function ( q ) { - - return 2 * Math.acos( Math.abs( MathUtils.clamp( this.dot( q ), - 1, 1 ) ) ); - - }, - - rotateTowards: function ( q, step ) { - - var angle = this.angleTo( q ); - - if ( angle === 0 ) { return this; } - - var t = Math.min( 1, step / angle ); - - this.slerp( q, t ); - - return this; - - }, - - inverse: function () { - - // quaternion is assumed to have unit length - - return this.conjugate(); - - }, - - conjugate: function () { - - this._x *= - 1; - this._y *= - 1; - this._z *= - 1; - - this._onChangeCallback(); - - return this; - - }, - - dot: function ( v ) { - - return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; - - }, - - lengthSq: function () { - - return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; - - }, - - length: function () { - - return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); - - }, - - normalize: function () { - - var l = this.length(); - - if ( l === 0 ) { - - this._x = 0; - this._y = 0; - this._z = 0; - this._w = 1; - - } else { - - l = 1 / l; - - this._x = this._x * l; - this._y = this._y * l; - this._z = this._z * l; - this._w = this._w * l; - - } - - this._onChangeCallback(); - - return this; - - }, - - multiply: function ( q, p ) { - - if ( p !== undefined ) { - - console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); - return this.multiplyQuaternions( q, p ); - - } - - return this.multiplyQuaternions( this, q ); - - }, - - premultiply: function ( q ) { - - return this.multiplyQuaternions( q, this ); - - }, - - multiplyQuaternions: function ( a, b ) { - - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - - var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; - var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; - - this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; - this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; - this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; - this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; - - this._onChangeCallback(); - - return this; - - }, - - slerp: function ( qb, t ) { - - if ( t === 0 ) { return this; } - if ( t === 1 ) { return this.copy( qb ); } - - var x = this._x, y = this._y, z = this._z, w = this._w; - - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - - var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; - - if ( cosHalfTheta < 0 ) { - - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; - - cosHalfTheta = - cosHalfTheta; - - } else { - - this.copy( qb ); - - } - - if ( cosHalfTheta >= 1.0 ) { - - this._w = w; - this._x = x; - this._y = y; - this._z = z; - - return this; - - } - - var sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; - - if ( sqrSinHalfTheta <= Number.EPSILON ) { - - var s = 1 - t; - this._w = s * w + t * this._w; - this._x = s * x + t * this._x; - this._y = s * y + t * this._y; - this._z = s * z + t * this._z; - - this.normalize(); - this._onChangeCallback(); - - return this; - - } - - var sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); - var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, - ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - - this._w = ( w * ratioA + this._w * ratioB ); - this._x = ( x * ratioA + this._x * ratioB ); - this._y = ( y * ratioA + this._y * ratioB ); - this._z = ( z * ratioA + this._z * ratioB ); - - this._onChangeCallback(); - - return this; - - }, - - equals: function ( quaternion ) { - - return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) { offset = 0; } - - this._x = array[ offset ]; - this._y = array[ offset + 1 ]; - this._z = array[ offset + 2 ]; - this._w = array[ offset + 3 ]; - - this._onChangeCallback(); - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) { array = []; } - if ( offset === undefined ) { offset = 0; } - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._w; - - return array; - - }, - - fromBufferAttribute: function ( attribute, index ) { - - this._x = attribute.getX( index ); - this._y = attribute.getY( index ); - this._z = attribute.getZ( index ); - this._w = attribute.getW( index ); - - return this; - - }, - - _onChange: function ( callback ) { - - this._onChangeCallback = callback; - - return this; - - }, - - _onChangeCallback: function () {} - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author kile / http://kile.stravaganza.org/ - * @author philogb / http://blog.thejit.org/ - * @author mikael emtinger / http://gomo.se/ - * @author egraether / http://egraether.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - var _vector = new Vector3(); - var _quaternion = new Quaternion(); - - function Vector3( x, y, z ) { - - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - - } - - Object.assign( Vector3.prototype, { - - isVector3: true, - - set: function ( x, y, z ) { - - this.x = x; - this.y = y; - this.z = z; - - return this; - - }, - - setScalar: function ( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; - - return this; - - }, - - setX: function ( x ) { - - this.x = x; - - return this; - - }, - - setY: function ( y ) { - - this.y = y; - - return this; - - }, - - setZ: function ( z ) { - - this.z = z; - - return this; - - }, - - setComponent: function ( index, value ) { - - switch ( index ) { - - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - default: throw new Error( 'index is out of range: ' + index ); - - } - - return this; - - }, - - getComponent: function ( index ) { - - switch ( index ) { - - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - default: throw new Error( 'index is out of range: ' + index ); - - } - - }, - - clone: function () { - - return new this.constructor( this.x, this.y, this.z ); - - }, - - copy: function ( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; - - return this; - - }, - - add: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); - - } - - this.x += v.x; - this.y += v.y; - this.z += v.z; - - return this; - - }, - - addScalar: function ( s ) { - - this.x += s; - this.y += s; - this.z += s; - - return this; - - }, - - addVectors: function ( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - - return this; - - }, - - addScaledVector: function ( v, s ) { - - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - - return this; - - }, - - sub: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } - - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - - return this; - - }, - - subScalar: function ( s ) { - - this.x -= s; - this.y -= s; - this.z -= s; - - return this; - - }, - - subVectors: function ( a, b ) { - - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - - return this; - - }, - - multiply: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); - return this.multiplyVectors( v, w ); - - } - - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; - - return this; - - }, - - multiplyScalar: function ( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - - return this; - - }, - - multiplyVectors: function ( a, b ) { - - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; - - return this; - - }, - - applyEuler: function ( euler ) { - - if ( ! ( euler && euler.isEuler ) ) { - - console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - - } - - return this.applyQuaternion( _quaternion.setFromEuler( euler ) ); - - }, - - applyAxisAngle: function ( axis, angle ) { - - return this.applyQuaternion( _quaternion.setFromAxisAngle( axis, angle ) ); - - }, - - applyMatrix3: function ( m ) { - - var x = this.x, y = this.y, z = this.z; - var e = m.elements; - - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; - this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; - - return this; - - }, - - applyNormalMatrix: function ( m ) { - - return this.applyMatrix3( m ).normalize(); - - }, - - applyMatrix4: function ( m ) { - - var x = this.x, y = this.y, z = this.z; - var e = m.elements; - - var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); - - this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; - this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; - this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; - - return this; - - }, - - applyQuaternion: function ( q ) { - - var x = this.x, y = this.y, z = this.z; - var qx = q.x, qy = q.y, qz = q.z, qw = q.w; - - // calculate quat * vector - - var ix = qw * x + qy * z - qz * y; - var iy = qw * y + qz * x - qx * z; - var iz = qw * z + qx * y - qy * x; - var iw = - qx * x - qy * y - qz * z; - - // calculate result * inverse quat - - this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; - this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; - this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; - - return this; - - }, - - project: function ( camera ) { - - return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); - - }, - - unproject: function ( camera ) { - - return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); - - }, - - transformDirection: function ( m ) { - - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction - - var x = this.x, y = this.y, z = this.z; - var e = m.elements; - - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; - - return this.normalize(); - - }, - - divide: function ( v ) { - - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; - - return this; - - }, - - divideScalar: function ( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - }, - - min: function ( v ) { - - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - - return this; - - }, - - max: function ( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - - return this; - - }, - - clamp: function ( min, max ) { - - // assumes min < max, componentwise - - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - - return this; - - }, - - clampScalar: function ( minVal, maxVal ) { - - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - - return this; - - }, - - clampLength: function ( min, max ) { - - var length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - }, - - floor: function () { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - - return this; - - }, - - ceil: function () { - - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - - return this; - - }, - - round: function () { - - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - - return this; - - }, - - roundToZero: function () { - - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - - return this; - - }, - - negate: function () { - - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - - return this; - - }, - - dot: function ( v ) { - - return this.x * v.x + this.y * v.y + this.z * v.z; - - }, - - // TODO lengthSquared? - - lengthSq: function () { - - return this.x * this.x + this.y * this.y + this.z * this.z; - - }, - - length: function () { - - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); - - }, - - manhattanLength: function () { - - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - - }, - - normalize: function () { - - return this.divideScalar( this.length() || 1 ); - - }, - - setLength: function ( length ) { - - return this.normalize().multiplyScalar( length ); - - }, - - lerp: function ( v, alpha ) { - - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - - return this; - - }, - - lerpVectors: function ( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - - return this; - - }, - - cross: function ( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); - - } - - return this.crossVectors( this, v ); - - }, - - crossVectors: function ( a, b ) { - - var ax = a.x, ay = a.y, az = a.z; - var bx = b.x, by = b.y, bz = b.z; - - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; - - return this; - - }, - - projectOnVector: function ( v ) { - - var denominator = v.lengthSq(); - - if ( denominator === 0 ) { return this.set( 0, 0, 0 ); } - - var scalar = v.dot( this ) / denominator; - - return this.copy( v ).multiplyScalar( scalar ); - - }, - - projectOnPlane: function ( planeNormal ) { - - _vector.copy( this ).projectOnVector( planeNormal ); - - return this.sub( _vector ); - - }, - - reflect: function ( normal ) { - - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length - - return this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - - }, - - angleTo: function ( v ) { - - var denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); - - if ( denominator === 0 ) { return Math.PI / 2; } - - var theta = this.dot( v ) / denominator; - - // clamp, to handle numerical problems - - return Math.acos( MathUtils.clamp( theta, - 1, 1 ) ); - - }, - - distanceTo: function ( v ) { - - return Math.sqrt( this.distanceToSquared( v ) ); - - }, - - distanceToSquared: function ( v ) { - - var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; - - return dx * dx + dy * dy + dz * dz; - - }, - - manhattanDistanceTo: function ( v ) { - - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); - - }, - - setFromSpherical: function ( s ) { - - return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); - - }, - - setFromSphericalCoords: function ( radius, phi, theta ) { - - var sinPhiRadius = Math.sin( phi ) * radius; - - this.x = sinPhiRadius * Math.sin( theta ); - this.y = Math.cos( phi ) * radius; - this.z = sinPhiRadius * Math.cos( theta ); - - return this; - - }, - - setFromCylindrical: function ( c ) { - - return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); - - }, - - setFromCylindricalCoords: function ( radius, theta, y ) { - - this.x = radius * Math.sin( theta ); - this.y = y; - this.z = radius * Math.cos( theta ); - - return this; - - }, - - setFromMatrixPosition: function ( m ) { - - var e = m.elements; - - this.x = e[ 12 ]; - this.y = e[ 13 ]; - this.z = e[ 14 ]; - - return this; - - }, - - setFromMatrixScale: function ( m ) { - - var sx = this.setFromMatrixColumn( m, 0 ).length(); - var sy = this.setFromMatrixColumn( m, 1 ).length(); - var sz = this.setFromMatrixColumn( m, 2 ).length(); - - this.x = sx; - this.y = sy; - this.z = sz; - - return this; - - }, - - setFromMatrixColumn: function ( m, index ) { - - return this.fromArray( m.elements, index * 4 ); - - }, - - setFromMatrix3Column: function ( m, index ) { - - return this.fromArray( m.elements, index * 3 ); - - }, - - equals: function ( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) { offset = 0; } - - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) { array = []; } - if ( offset === undefined ) { offset = 0; } - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - - return array; - - }, - - fromBufferAttribute: function ( attribute, index, offset ) { - - if ( offset !== undefined ) { - - console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); - - } - - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - - return this; - - }, - - random: function () { - - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - - return this; - - } - - } ); - - var _v1 = new Vector3(); - var _m1 = new Matrix4(); - var _zero = new Vector3( 0, 0, 0 ); - var _one = new Vector3( 1, 1, 1 ); - var _x = new Vector3(); - var _y = new Vector3(); - var _z = new Vector3(); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author philogb / http://blog.thejit.org/ - * @author jordi_ros / http://plattsoft.com - * @author D1plo1d / http://github.com/D1plo1d - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author timknip / http://www.floorplanner.com/ - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - */ - - function Matrix4() { - - this.elements = [ - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ]; - - if ( arguments.length > 0 ) { - - console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); - - } - - } - - Object.assign( Matrix4.prototype, { - - isMatrix4: true, - - set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - - var te = this.elements; - - te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; - te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; - te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; - te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; - - return this; - - }, - - identity: function () { - - this.set( - - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - clone: function () { - - return new Matrix4().fromArray( this.elements ); - - }, - - copy: function ( m ) { - - var te = this.elements; - var me = m.elements; - - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; - te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; - te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; - te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; - - return this; - - }, - - copyPosition: function ( m ) { - - var te = this.elements, me = m.elements; - - te[ 12 ] = me[ 12 ]; - te[ 13 ] = me[ 13 ]; - te[ 14 ] = me[ 14 ]; - - return this; - - }, - - extractBasis: function ( xAxis, yAxis, zAxis ) { - - xAxis.setFromMatrixColumn( this, 0 ); - yAxis.setFromMatrixColumn( this, 1 ); - zAxis.setFromMatrixColumn( this, 2 ); - - return this; - - }, - - makeBasis: function ( xAxis, yAxis, zAxis ) { - - this.set( - xAxis.x, yAxis.x, zAxis.x, 0, - xAxis.y, yAxis.y, zAxis.y, 0, - xAxis.z, yAxis.z, zAxis.z, 0, - 0, 0, 0, 1 - ); - - return this; - - }, - - extractRotation: function ( m ) { - - // this method does not support reflection matrices - - var te = this.elements; - var me = m.elements; - - var scaleX = 1 / _v1.setFromMatrixColumn( m, 0 ).length(); - var scaleY = 1 / _v1.setFromMatrixColumn( m, 1 ).length(); - var scaleZ = 1 / _v1.setFromMatrixColumn( m, 2 ).length(); - - te[ 0 ] = me[ 0 ] * scaleX; - te[ 1 ] = me[ 1 ] * scaleX; - te[ 2 ] = me[ 2 ] * scaleX; - te[ 3 ] = 0; - - te[ 4 ] = me[ 4 ] * scaleY; - te[ 5 ] = me[ 5 ] * scaleY; - te[ 6 ] = me[ 6 ] * scaleY; - te[ 7 ] = 0; - - te[ 8 ] = me[ 8 ] * scaleZ; - te[ 9 ] = me[ 9 ] * scaleZ; - te[ 10 ] = me[ 10 ] * scaleZ; - te[ 11 ] = 0; - - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - }, - - makeRotationFromEuler: function ( euler ) { - - if ( ! ( euler && euler.isEuler ) ) { - - console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); - - } - - var te = this.elements; - - var x = euler.x, y = euler.y, z = euler.z; - var a = Math.cos( x ), b = Math.sin( x ); - var c = Math.cos( y ), d = Math.sin( y ); - var e = Math.cos( z ), f = Math.sin( z ); - - if ( euler.order === 'XYZ' ) { - - var ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = - c * f; - te[ 8 ] = d; - - te[ 1 ] = af + be * d; - te[ 5 ] = ae - bf * d; - te[ 9 ] = - b * c; - - te[ 2 ] = bf - ae * d; - te[ 6 ] = be + af * d; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YXZ' ) { - - var ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce + df * b; - te[ 4 ] = de * b - cf; - te[ 8 ] = a * d; - - te[ 1 ] = a * f; - te[ 5 ] = a * e; - te[ 9 ] = - b; - - te[ 2 ] = cf * b - de; - te[ 6 ] = df + ce * b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZXY' ) { - - var ce = c * e, cf = c * f, de = d * e, df = d * f; - - te[ 0 ] = ce - df * b; - te[ 4 ] = - a * f; - te[ 8 ] = de + cf * b; - - te[ 1 ] = cf + de * b; - te[ 5 ] = a * e; - te[ 9 ] = df - ce * b; - - te[ 2 ] = - a * d; - te[ 6 ] = b; - te[ 10 ] = a * c; - - } else if ( euler.order === 'ZYX' ) { - - var ae = a * e, af = a * f, be = b * e, bf = b * f; - - te[ 0 ] = c * e; - te[ 4 ] = be * d - af; - te[ 8 ] = ae * d + bf; - - te[ 1 ] = c * f; - te[ 5 ] = bf * d + ae; - te[ 9 ] = af * d - be; - - te[ 2 ] = - d; - te[ 6 ] = b * c; - te[ 10 ] = a * c; - - } else if ( euler.order === 'YZX' ) { - - var ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = bd - ac * f; - te[ 8 ] = bc * f + ad; - - te[ 1 ] = f; - te[ 5 ] = a * e; - te[ 9 ] = - b * e; - - te[ 2 ] = - d * e; - te[ 6 ] = ad * f + bc; - te[ 10 ] = ac - bd * f; - - } else if ( euler.order === 'XZY' ) { - - var ac = a * c, ad = a * d, bc = b * c, bd = b * d; - - te[ 0 ] = c * e; - te[ 4 ] = - f; - te[ 8 ] = d * e; - - te[ 1 ] = ac * f + bd; - te[ 5 ] = a * e; - te[ 9 ] = ad * f - bc; - - te[ 2 ] = bc * f - ad; - te[ 6 ] = b * e; - te[ 10 ] = bd * f + ac; - - } - - // bottom row - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; - - // last column - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; - - return this; - - }, - - makeRotationFromQuaternion: function ( q ) { - - return this.compose( _zero, q, _one ); - - }, - - lookAt: function ( eye, target, up ) { - - var te = this.elements; - - _z.subVectors( eye, target ); - - if ( _z.lengthSq() === 0 ) { - - // eye and target are in the same position - - _z.z = 1; - - } - - _z.normalize(); - _x.crossVectors( up, _z ); - - if ( _x.lengthSq() === 0 ) { - - // up and z are parallel - - if ( Math.abs( up.z ) === 1 ) { - - _z.x += 0.0001; - - } else { - - _z.z += 0.0001; - - } - - _z.normalize(); - _x.crossVectors( up, _z ); - - } - - _x.normalize(); - _y.crossVectors( _z, _x ); - - te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; - te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; - te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; - - return this; - - }, - - multiply: function ( m, n ) { - - if ( n !== undefined ) { - - console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); - return this.multiplyMatrices( m, n ); - - } - - return this.multiplyMatrices( this, m ); - - }, - - premultiply: function ( m ) { - - return this.multiplyMatrices( m, this ); - - }, - - multiplyMatrices: function ( a, b ) { - - var ae = a.elements; - var be = b.elements; - var te = this.elements; - - var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; - var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; - var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; - var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; - - var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; - var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; - var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; - var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; - - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; - te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; - te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; - te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; - - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; - te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; - te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; - te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; - - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; - te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; - te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; - te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; - - te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; - te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; - te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; - te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; - - return this; - - }, - - multiplyScalar: function ( s ) { - - var te = this.elements; - - te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; - te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; - te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; - te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; - - return this; - - }, - - determinant: function () { - - var te = this.elements; - - var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; - var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; - var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; - var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - - //TODO: make this more efficient - //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) - - return ( - n41 * ( - + n14 * n23 * n32 - - n13 * n24 * n32 - - n14 * n22 * n33 - + n12 * n24 * n33 - + n13 * n22 * n34 - - n12 * n23 * n34 - ) + - n42 * ( - + n11 * n23 * n34 - - n11 * n24 * n33 - + n14 * n21 * n33 - - n13 * n21 * n34 - + n13 * n24 * n31 - - n14 * n23 * n31 - ) + - n43 * ( - + n11 * n24 * n32 - - n11 * n22 * n34 - - n14 * n21 * n32 - + n12 * n21 * n34 - + n14 * n22 * n31 - - n12 * n24 * n31 - ) + - n44 * ( - - n13 * n22 * n31 - - n11 * n23 * n32 - + n11 * n22 * n33 - + n13 * n21 * n32 - - n12 * n21 * n33 - + n12 * n23 * n31 - ) - - ); - - }, - - transpose: function () { - - var te = this.elements; - var tmp; - - tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; - tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; - tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; - - tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; - tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; - tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; - - return this; - - }, - - setPosition: function ( x, y, z ) { - - var te = this.elements; - - if ( x.isVector3 ) { - - te[ 12 ] = x.x; - te[ 13 ] = x.y; - te[ 14 ] = x.z; - - } else { - - te[ 12 ] = x; - te[ 13 ] = y; - te[ 14 ] = z; - - } - - return this; - - }, - - getInverse: function ( m, throwOnDegenerate ) { - - if ( throwOnDegenerate !== undefined ) { - - console.warn( "THREE.Matrix4: .getInverse() can no longer be configured to throw on degenerate." ); - - } - - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - var te = this.elements, - me = m.elements, - - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], - n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], - n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], - n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], - - t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, - t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, - t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, - t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; - - var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; - - if ( det === 0 ) { return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); } - - var detInv = 1 / det; - - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; - te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; - te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; - - te[ 4 ] = t12 * detInv; - te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; - te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; - te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; - - te[ 8 ] = t13 * detInv; - te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; - te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; - te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; - - te[ 12 ] = t14 * detInv; - te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; - te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; - te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; - - return this; - - }, - - scale: function ( v ) { - - var te = this.elements; - var x = v.x, y = v.y, z = v.z; - - te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; - te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; - te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; - te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; - - return this; - - }, - - getMaxScaleOnAxis: function () { - - var te = this.elements; - - var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; - var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; - var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - - return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); - - }, - - makeTranslation: function ( x, y, z ) { - - this.set( - - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeRotationX: function ( theta ) { - - var c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - 1, 0, 0, 0, - 0, c, - s, 0, - 0, s, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeRotationY: function ( theta ) { - - var c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, 0, s, 0, - 0, 1, 0, 0, - - s, 0, c, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeRotationZ: function ( theta ) { - - var c = Math.cos( theta ), s = Math.sin( theta ); - - this.set( - - c, - s, 0, 0, - s, c, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeRotationAxis: function ( axis, angle ) { - - // Based on http://www.gamedev.net/reference/articles/article1199.asp - - var c = Math.cos( angle ); - var s = Math.sin( angle ); - var t = 1 - c; - var x = axis.x, y = axis.y, z = axis.z; - var tx = t * x, ty = t * y; - - this.set( - - tx * x + c, tx * y - s * z, tx * z + s * y, 0, - tx * y + s * z, ty * y + c, ty * z - s * x, 0, - tx * z - s * y, ty * z + s * x, t * z * z + c, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeScale: function ( x, y, z ) { - - this.set( - - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - makeShear: function ( x, y, z ) { - - this.set( - - 1, y, z, 0, - x, 1, z, 0, - x, y, 1, 0, - 0, 0, 0, 1 - - ); - - return this; - - }, - - compose: function ( position, quaternion, scale ) { - - var te = this.elements; - - var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; - var x2 = x + x, y2 = y + y, z2 = z + z; - var xx = x * x2, xy = x * y2, xz = x * z2; - var yy = y * y2, yz = y * z2, zz = z * z2; - var wx = w * x2, wy = w * y2, wz = w * z2; - - var sx = scale.x, sy = scale.y, sz = scale.z; - - te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; - te[ 1 ] = ( xy + wz ) * sx; - te[ 2 ] = ( xz - wy ) * sx; - te[ 3 ] = 0; - - te[ 4 ] = ( xy - wz ) * sy; - te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; - te[ 6 ] = ( yz + wx ) * sy; - te[ 7 ] = 0; - - te[ 8 ] = ( xz + wy ) * sz; - te[ 9 ] = ( yz - wx ) * sz; - te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; - te[ 11 ] = 0; - - te[ 12 ] = position.x; - te[ 13 ] = position.y; - te[ 14 ] = position.z; - te[ 15 ] = 1; - - return this; - - }, - - decompose: function ( position, quaternion, scale ) { - - var te = this.elements; - - var sx = _v1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - var sy = _v1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - var sz = _v1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); - - // if determine is negative, we need to invert one scale - var det = this.determinant(); - if ( det < 0 ) { sx = - sx; } - - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; - - // scale the rotation part - _m1.copy( this ); - - var invSX = 1 / sx; - var invSY = 1 / sy; - var invSZ = 1 / sz; - - _m1.elements[ 0 ] *= invSX; - _m1.elements[ 1 ] *= invSX; - _m1.elements[ 2 ] *= invSX; - - _m1.elements[ 4 ] *= invSY; - _m1.elements[ 5 ] *= invSY; - _m1.elements[ 6 ] *= invSY; - - _m1.elements[ 8 ] *= invSZ; - _m1.elements[ 9 ] *= invSZ; - _m1.elements[ 10 ] *= invSZ; - - quaternion.setFromRotationMatrix( _m1 ); - - scale.x = sx; - scale.y = sy; - scale.z = sz; - - return this; - - }, - - makePerspective: function ( left, right, top, bottom, near, far ) { - - if ( far === undefined ) { - - console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); - - } - - var te = this.elements; - var x = 2 * near / ( right - left ); - var y = 2 * near / ( top - bottom ); - - var a = ( right + left ) / ( right - left ); - var b = ( top + bottom ) / ( top - bottom ); - var c = - ( far + near ) / ( far - near ); - var d = - 2 * far * near / ( far - near ); - - te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; - te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; - - return this; - - }, - - makeOrthographic: function ( left, right, top, bottom, near, far ) { - - var te = this.elements; - var w = 1.0 / ( right - left ); - var h = 1.0 / ( top - bottom ); - var p = 1.0 / ( far - near ); - - var x = ( right + left ) * w; - var y = ( top + bottom ) * h; - var z = ( far + near ) * p; - - te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; - te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; - - return this; - - }, - - equals: function ( matrix ) { - - var te = this.elements; - var me = matrix.elements; - - for ( var i = 0; i < 16; i ++ ) { - - if ( te[ i ] !== me[ i ] ) { return false; } - - } - - return true; - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) { offset = 0; } - - for ( var i = 0; i < 16; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) { array = []; } - if ( offset === undefined ) { offset = 0; } - - var te = this.elements; - - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - array[ offset + 3 ] = te[ 3 ]; - - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - - array[ offset + 8 ] = te[ 8 ]; - array[ offset + 9 ] = te[ 9 ]; - array[ offset + 10 ] = te[ 10 ]; - array[ offset + 11 ] = te[ 11 ]; - - array[ offset + 12 ] = te[ 12 ]; - array[ offset + 13 ] = te[ 13 ]; - array[ offset + 14 ] = te[ 14 ]; - array[ offset + 15 ] = te[ 15 ]; - - return array; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - */ - - var _matrix = new Matrix4(); - var _quaternion$1 = new Quaternion(); - - function Euler( x, y, z, order ) { - - this._x = x || 0; - this._y = y || 0; - this._z = z || 0; - this._order = order || Euler.DefaultOrder; - - } - - Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; - - Euler.DefaultOrder = 'XYZ'; - - Object.defineProperties( Euler.prototype, { - - x: { - - get: function () { - - return this._x; - - }, - - set: function ( value ) { - - this._x = value; - this._onChangeCallback(); - - } - - }, - - y: { - - get: function () { - - return this._y; - - }, - - set: function ( value ) { - - this._y = value; - this._onChangeCallback(); - - } - - }, - - z: { - - get: function () { - - return this._z; - - }, - - set: function ( value ) { - - this._z = value; - this._onChangeCallback(); - - } - - }, - - order: { - - get: function () { - - return this._order; - - }, - - set: function ( value ) { - - this._order = value; - this._onChangeCallback(); - - } - - } - - } ); - - Object.assign( Euler.prototype, { - - isEuler: true, - - set: function ( x, y, z, order ) { - - this._x = x; - this._y = y; - this._z = z; - this._order = order || this._order; - - this._onChangeCallback(); - - return this; - - }, - - clone: function () { - - return new this.constructor( this._x, this._y, this._z, this._order ); - - }, - - copy: function ( euler ) { - - this._x = euler._x; - this._y = euler._y; - this._z = euler._z; - this._order = euler._order; - - this._onChangeCallback(); - - return this; - - }, - - setFromRotationMatrix: function ( m, order, update ) { - - var clamp = MathUtils.clamp; - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - var te = m.elements; - var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; - var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; - var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - - order = order || this._order; - - switch ( order ) { - - case 'XYZ': - - this._y = Math.asin( clamp( m13, - 1, 1 ) ); - - if ( Math.abs( m13 ) < 0.9999999 ) { - - this._x = Math.atan2( - m23, m33 ); - this._z = Math.atan2( - m12, m11 ); - - } else { - - this._x = Math.atan2( m32, m22 ); - this._z = 0; - - } - - break; - - case 'YXZ': - - this._x = Math.asin( - clamp( m23, - 1, 1 ) ); - - if ( Math.abs( m23 ) < 0.9999999 ) { - - this._y = Math.atan2( m13, m33 ); - this._z = Math.atan2( m21, m22 ); - - } else { - - this._y = Math.atan2( - m31, m11 ); - this._z = 0; - - } - - break; - - case 'ZXY': - - this._x = Math.asin( clamp( m32, - 1, 1 ) ); - - if ( Math.abs( m32 ) < 0.9999999 ) { - - this._y = Math.atan2( - m31, m33 ); - this._z = Math.atan2( - m12, m22 ); - - } else { - - this._y = 0; - this._z = Math.atan2( m21, m11 ); - - } - - break; - - case 'ZYX': - - this._y = Math.asin( - clamp( m31, - 1, 1 ) ); - - if ( Math.abs( m31 ) < 0.9999999 ) { - - this._x = Math.atan2( m32, m33 ); - this._z = Math.atan2( m21, m11 ); - - } else { - - this._x = 0; - this._z = Math.atan2( - m12, m22 ); - - } - - break; - - case 'YZX': - - this._z = Math.asin( clamp( m21, - 1, 1 ) ); - - if ( Math.abs( m21 ) < 0.9999999 ) { - - this._x = Math.atan2( - m23, m22 ); - this._y = Math.atan2( - m31, m11 ); - - } else { - - this._x = 0; - this._y = Math.atan2( m13, m33 ); - - } - - break; - - case 'XZY': - - this._z = Math.asin( - clamp( m12, - 1, 1 ) ); - - if ( Math.abs( m12 ) < 0.9999999 ) { - - this._x = Math.atan2( m32, m22 ); - this._y = Math.atan2( m13, m11 ); - - } else { - - this._x = Math.atan2( - m23, m33 ); - this._y = 0; - - } - - break; - - default: - - console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); - - } - - this._order = order; - - if ( update !== false ) { this._onChangeCallback(); } - - return this; - - }, - - setFromQuaternion: function ( q, order, update ) { - - _matrix.makeRotationFromQuaternion( q ); - - return this.setFromRotationMatrix( _matrix, order, update ); - - }, - - setFromVector3: function ( v, order ) { - - return this.set( v.x, v.y, v.z, order || this._order ); - - }, - - reorder: function ( newOrder ) { - - // WARNING: this discards revolution information -bhouston - - _quaternion$1.setFromEuler( this ); - - return this.setFromQuaternion( _quaternion$1, newOrder ); - - }, - - equals: function ( euler ) { - - return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); - - }, - - fromArray: function ( array ) { - - this._x = array[ 0 ]; - this._y = array[ 1 ]; - this._z = array[ 2 ]; - if ( array[ 3 ] !== undefined ) { this._order = array[ 3 ]; } - - this._onChangeCallback(); - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) { array = []; } - if ( offset === undefined ) { offset = 0; } - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._order; - - return array; - - }, - - toVector3: function ( optionalResult ) { - - if ( optionalResult ) { - - return optionalResult.set( this._x, this._y, this._z ); - - } else { - - return new Vector3( this._x, this._y, this._z ); - - } - - }, - - _onChange: function ( callback ) { - - this._onChangeCallback = callback; - - return this; - - }, - - _onChangeCallback: function () {} - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Layers() { - - this.mask = 1 | 0; - - } - - Object.assign( Layers.prototype, { - - set: function ( channel ) { - - this.mask = 1 << channel | 0; - - }, - - enable: function ( channel ) { - - this.mask |= 1 << channel | 0; - - }, - - enableAll: function () { - - this.mask = 0xffffffff | 0; - - }, - - toggle: function ( channel ) { - - this.mask ^= 1 << channel | 0; - - }, - - disable: function ( channel ) { - - this.mask &= ~ ( 1 << channel | 0 ); - - }, - - disableAll: function () { - - this.mask = 0; - - }, - - test: function ( layers ) { - - return ( this.mask & layers.mask ) !== 0; - - } - - } ); - - var _object3DId = 0; - - var _v1$1 = new Vector3(); - var _q1 = new Quaternion(); - var _m1$1 = new Matrix4(); - var _target = new Vector3(); - - var _position = new Vector3(); - var _scale = new Vector3(); - var _quaternion$2 = new Quaternion(); - - var _xAxis = new Vector3( 1, 0, 0 ); - var _yAxis = new Vector3( 0, 1, 0 ); - var _zAxis = new Vector3( 0, 0, 1 ); - - var _addedEvent = { type: 'added' }; - var _removedEvent = { type: 'removed' }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author elephantatwork / www.elephantatwork.ch - */ - - function Object3D() { - - Object.defineProperty( this, 'id', { value: _object3DId ++ } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'Object3D'; - - this.parent = null; - this.children = []; - - this.up = Object3D.DefaultUp.clone(); - - var position = new Vector3(); - var rotation = new Euler(); - var quaternion = new Quaternion(); - var scale = new Vector3( 1, 1, 1 ); - - function onRotationChange() { - - quaternion.setFromEuler( rotation, false ); - - } - - function onQuaternionChange() { - - rotation.setFromQuaternion( quaternion, undefined, false ); - - } - - rotation._onChange( onRotationChange ); - quaternion._onChange( onQuaternionChange ); - - Object.defineProperties( this, { - position: { - configurable: true, - enumerable: true, - value: position - }, - rotation: { - configurable: true, - enumerable: true, - value: rotation - }, - quaternion: { - configurable: true, - enumerable: true, - value: quaternion - }, - scale: { - configurable: true, - enumerable: true, - value: scale - }, - modelViewMatrix: { - value: new Matrix4() - }, - normalMatrix: { - value: new Matrix3() - } - } ); - - this.matrix = new Matrix4(); - this.matrixWorld = new Matrix4(); - - this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; - this.matrixWorldNeedsUpdate = false; - - this.layers = new Layers(); - this.visible = true; - - this.castShadow = false; - this.receiveShadow = false; - - this.frustumCulled = true; - this.renderOrder = 0; - - this.userData = {}; - - } - - Object3D.DefaultUp = new Vector3( 0, 1, 0 ); - Object3D.DefaultMatrixAutoUpdate = true; - - Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Object3D, - - isObject3D: true, - - onBeforeRender: function () {}, - onAfterRender: function () {}, - - applyMatrix4: function ( matrix ) { - - if ( this.matrixAutoUpdate ) { this.updateMatrix(); } - - this.matrix.premultiply( matrix ); - - this.matrix.decompose( this.position, this.quaternion, this.scale ); - - }, - - applyQuaternion: function ( q ) { - - this.quaternion.premultiply( q ); - - return this; - - }, - - setRotationFromAxisAngle: function ( axis, angle ) { - - // assumes axis is normalized - - this.quaternion.setFromAxisAngle( axis, angle ); - - }, - - setRotationFromEuler: function ( euler ) { - - this.quaternion.setFromEuler( euler, true ); - - }, - - setRotationFromMatrix: function ( m ) { - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - this.quaternion.setFromRotationMatrix( m ); - - }, - - setRotationFromQuaternion: function ( q ) { - - // assumes q is normalized - - this.quaternion.copy( q ); - - }, - - rotateOnAxis: function ( axis, angle ) { - - // rotate object on axis in object space - // axis is assumed to be normalized - - _q1.setFromAxisAngle( axis, angle ); - - this.quaternion.multiply( _q1 ); - - return this; - - }, - - rotateOnWorldAxis: function ( axis, angle ) { - - // rotate object on axis in world space - // axis is assumed to be normalized - // method assumes no rotated parent - - _q1.setFromAxisAngle( axis, angle ); - - this.quaternion.premultiply( _q1 ); - - return this; - - }, - - rotateX: function ( angle ) { - - return this.rotateOnAxis( _xAxis, angle ); - - }, - - rotateY: function ( angle ) { - - return this.rotateOnAxis( _yAxis, angle ); - - }, - - rotateZ: function ( angle ) { - - return this.rotateOnAxis( _zAxis, angle ); - - }, - - translateOnAxis: function ( axis, distance ) { - - // translate object by distance along axis in object space - // axis is assumed to be normalized - - _v1$1.copy( axis ).applyQuaternion( this.quaternion ); - - this.position.add( _v1$1.multiplyScalar( distance ) ); - - return this; - - }, - - translateX: function ( distance ) { - - return this.translateOnAxis( _xAxis, distance ); - - }, - - translateY: function ( distance ) { - - return this.translateOnAxis( _yAxis, distance ); - - }, - - translateZ: function ( distance ) { - - return this.translateOnAxis( _zAxis, distance ); - - }, - - localToWorld: function ( vector ) { - - return vector.applyMatrix4( this.matrixWorld ); - - }, - - worldToLocal: function ( vector ) { - - return vector.applyMatrix4( _m1$1.getInverse( this.matrixWorld ) ); - - }, - - lookAt: function ( x, y, z ) { - - // This method does not support objects having non-uniformly-scaled parent(s) - - if ( x.isVector3 ) { - - _target.copy( x ); - - } else { - - _target.set( x, y, z ); - - } - - var parent = this.parent; - - this.updateWorldMatrix( true, false ); - - _position.setFromMatrixPosition( this.matrixWorld ); - - if ( this.isCamera || this.isLight ) { - - _m1$1.lookAt( _position, _target, this.up ); - - } else { - - _m1$1.lookAt( _target, _position, this.up ); - - } - - this.quaternion.setFromRotationMatrix( _m1$1 ); - - if ( parent ) { - - _m1$1.extractRotation( parent.matrixWorld ); - _q1.setFromRotationMatrix( _m1$1 ); - this.quaternion.premultiply( _q1.inverse() ); - - } - - }, - - add: function ( object ) { - - if ( arguments.length > 1 ) { - - for ( var i = 0; i < arguments.length; i ++ ) { - - this.add( arguments[ i ] ); - - } - - return this; - - } - - if ( object === this ) { - - console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); - return this; - - } - - if ( ( object && object.isObject3D ) ) { - - if ( object.parent !== null ) { - - object.parent.remove( object ); - - } - - object.parent = this; - this.children.push( object ); - - object.dispatchEvent( _addedEvent ); - - } else { - - console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); - - } - - return this; - - }, - - remove: function ( object ) { - - if ( arguments.length > 1 ) { - - for ( var i = 0; i < arguments.length; i ++ ) { - - this.remove( arguments[ i ] ); - - } - - return this; - - } - - var index = this.children.indexOf( object ); - - if ( index !== - 1 ) { - - object.parent = null; - this.children.splice( index, 1 ); - - object.dispatchEvent( _removedEvent ); - - } - - return this; - - }, - - attach: function ( object ) { - - // adds object as a child of this, while maintaining the object's world transform - - this.updateWorldMatrix( true, false ); - - _m1$1.getInverse( this.matrixWorld ); - - if ( object.parent !== null ) { - - object.parent.updateWorldMatrix( true, false ); - - _m1$1.multiply( object.parent.matrixWorld ); - - } - - object.applyMatrix4( _m1$1 ); - - object.updateWorldMatrix( false, false ); - - this.add( object ); - - return this; - - }, - - getObjectById: function ( id ) { - - return this.getObjectByProperty( 'id', id ); - - }, - - getObjectByName: function ( name ) { - - return this.getObjectByProperty( 'name', name ); - - }, - - getObjectByProperty: function ( name, value ) { - - if ( this[ name ] === value ) { return this; } - - for ( var i = 0, l = this.children.length; i < l; i ++ ) { - - var child = this.children[ i ]; - var object = child.getObjectByProperty( name, value ); - - if ( object !== undefined ) { - - return object; - - } - - } - - return undefined; - - }, - - getWorldPosition: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - return target.setFromMatrixPosition( this.matrixWorld ); - - }, - - getWorldQuaternion: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' ); - target = new Quaternion(); - - } - - this.updateMatrixWorld( true ); - - this.matrixWorld.decompose( _position, target, _scale ); - - return target; - - }, - - getWorldScale: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldScale() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - this.matrixWorld.decompose( _position, _quaternion$2, target ); - - return target; - - }, - - getWorldDirection: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - var e = this.matrixWorld.elements; - - return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); - - }, - - raycast: function () {}, - - traverse: function ( callback ) { - - callback( this ); - - var children = this.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverse( callback ); - - } - - }, - - traverseVisible: function ( callback ) { - - if ( this.visible === false ) { return; } - - callback( this ); - - var children = this.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].traverseVisible( callback ); - - } - - }, - - traverseAncestors: function ( callback ) { - - var parent = this.parent; - - if ( parent !== null ) { - - callback( parent ); - - parent.traverseAncestors( callback ); - - } - - }, - - updateMatrix: function () { - - this.matrix.compose( this.position, this.quaternion, this.scale ); - - this.matrixWorldNeedsUpdate = true; - - }, - - updateMatrixWorld: function ( force ) { - - if ( this.matrixAutoUpdate ) { this.updateMatrix(); } - - if ( this.matrixWorldNeedsUpdate || force ) { - - if ( this.parent === null ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - this.matrixWorldNeedsUpdate = false; - - force = true; - - } - - // update children - - var children = this.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateMatrixWorld( force ); - - } - - }, - - updateWorldMatrix: function ( updateParents, updateChildren ) { - - var parent = this.parent; - - if ( updateParents === true && parent !== null ) { - - parent.updateWorldMatrix( true, false ); - - } - - if ( this.matrixAutoUpdate ) { this.updateMatrix(); } - - if ( this.parent === null ) { - - this.matrixWorld.copy( this.matrix ); - - } else { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - } - - // update children - - if ( updateChildren === true ) { - - var children = this.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateWorldMatrix( false, true ); - - } - - } - - }, - - toJSON: function ( meta ) { - - // meta is a string when called from JSON.stringify - var isRootObject = ( meta === undefined || typeof meta === 'string' ); - - var output = {}; - - // meta is a hash used to collect geometries, materials. - // not providing it implies that this is the root object - // being serialized. - if ( isRootObject ) { - - // initialize meta obj - meta = { - geometries: {}, - materials: {}, - textures: {}, - images: {}, - shapes: {} - }; - - output.metadata = { - version: 4.5, - type: 'Object', - generator: 'Object3D.toJSON' - }; - - } - - // standard Object3D serialization - - var object = {}; - - object.uuid = this.uuid; - object.type = this.type; - - if ( this.name !== '' ) { object.name = this.name; } - if ( this.castShadow === true ) { object.castShadow = true; } - if ( this.receiveShadow === true ) { object.receiveShadow = true; } - if ( this.visible === false ) { object.visible = false; } - if ( this.frustumCulled === false ) { object.frustumCulled = false; } - if ( this.renderOrder !== 0 ) { object.renderOrder = this.renderOrder; } - if ( JSON.stringify( this.userData ) !== '{}' ) { object.userData = this.userData; } - - object.layers = this.layers.mask; - object.matrix = this.matrix.toArray(); - - if ( this.matrixAutoUpdate === false ) { object.matrixAutoUpdate = false; } - - // object specific properties - - if ( this.isInstancedMesh ) { - - object.type = 'InstancedMesh'; - object.count = this.count; - object.instanceMatrix = this.instanceMatrix.toJSON(); - - } - - // - - function serialize( library, element ) { - - if ( library[ element.uuid ] === undefined ) { - - library[ element.uuid ] = element.toJSON( meta ); - - } - - return element.uuid; - - } - - if ( this.isMesh || this.isLine || this.isPoints ) { - - object.geometry = serialize( meta.geometries, this.geometry ); - - var parameters = this.geometry.parameters; - - if ( parameters !== undefined && parameters.shapes !== undefined ) { - - var shapes = parameters.shapes; - - if ( Array.isArray( shapes ) ) { - - for ( var i = 0, l = shapes.length; i < l; i ++ ) { - - var shape = shapes[ i ]; - - serialize( meta.shapes, shape ); - - } - - } else { - - serialize( meta.shapes, shapes ); - - } - - } - - } - - if ( this.material !== undefined ) { - - if ( Array.isArray( this.material ) ) { - - var uuids = []; - - for ( var i = 0, l = this.material.length; i < l; i ++ ) { - - uuids.push( serialize( meta.materials, this.material[ i ] ) ); - - } - - object.material = uuids; - - } else { - - object.material = serialize( meta.materials, this.material ); - - } - - } - - // - - if ( this.children.length > 0 ) { - - object.children = []; - - for ( var i = 0; i < this.children.length; i ++ ) { - - object.children.push( this.children[ i ].toJSON( meta ).object ); - - } - - } - - if ( isRootObject ) { - - var geometries = extractFromCache( meta.geometries ); - var materials = extractFromCache( meta.materials ); - var textures = extractFromCache( meta.textures ); - var images = extractFromCache( meta.images ); - var shapes = extractFromCache( meta.shapes ); - - if ( geometries.length > 0 ) { output.geometries = geometries; } - if ( materials.length > 0 ) { output.materials = materials; } - if ( textures.length > 0 ) { output.textures = textures; } - if ( images.length > 0 ) { output.images = images; } - if ( shapes.length > 0 ) { output.shapes = shapes; } - - } - - output.object = object; - - return output; - - // extract data from the cache hash - // remove metadata on each item - // and return as array - function extractFromCache( cache ) { - - var values = []; - for ( var key in cache ) { - - var data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - }, - - clone: function ( recursive ) { - - return new this.constructor().copy( this, recursive ); - - }, - - copy: function ( source, recursive ) { - - if ( recursive === undefined ) { recursive = true; } - - this.name = source.name; - - this.up.copy( source.up ); - - this.position.copy( source.position ); - this.quaternion.copy( source.quaternion ); - this.scale.copy( source.scale ); - - this.matrix.copy( source.matrix ); - this.matrixWorld.copy( source.matrixWorld ); - - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; - - this.layers.mask = source.layers.mask; - this.visible = source.visible; - - this.castShadow = source.castShadow; - this.receiveShadow = source.receiveShadow; - - this.frustumCulled = source.frustumCulled; - this.renderOrder = source.renderOrder; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - if ( recursive === true ) { - - for ( var i = 0; i < source.children.length; i ++ ) { - - var child = source.children[ i ]; - this.add( child.clone() ); - - } - - } - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Scene() { - - Object3D.call( this ); - - this.type = 'Scene'; - - this.background = null; - this.environment = null; - this.fog = null; - - this.overrideMaterial = null; - - this.autoUpdate = true; // checked by the renderer - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - - } - - } - - Scene.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Scene, - - isScene: true, - - copy: function ( source, recursive ) { - - Object3D.prototype.copy.call( this, source, recursive ); - - if ( source.background !== null ) { this.background = source.background.clone(); } - if ( source.environment !== null ) { this.environment = source.environment.clone(); } - if ( source.fog !== null ) { this.fog = source.fog.clone(); } - - if ( source.overrideMaterial !== null ) { this.overrideMaterial = source.overrideMaterial.clone(); } - - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; - - return this; - - }, - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - if ( this.background !== null ) { data.object.background = this.background.toJSON( meta ); } - if ( this.environment !== null ) { data.object.environment = this.environment.toJSON( meta ); } - if ( this.fog !== null ) { data.object.fog = this.fog.toJSON(); } - - return data; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - var _points = [ - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3() - ]; - - var _vector$1 = new Vector3(); - - var _box = new Box3(); - - // triangle centered vertices - - var _v0 = new Vector3(); - var _v1$2 = new Vector3(); - var _v2 = new Vector3(); - - // triangle edge vectors - - var _f0 = new Vector3(); - var _f1 = new Vector3(); - var _f2 = new Vector3(); - - var _center = new Vector3(); - var _extents = new Vector3(); - var _triangleNormal = new Vector3(); - var _testAxis = new Vector3(); - - /** - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - */ - - function Box3( min, max ) { - - this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); - - } - - - Object.assign( Box3.prototype, { - - isBox3: true, - - set: function ( min, max ) { - - this.min.copy( min ); - this.max.copy( max ); - - return this; - - }, - - setFromArray: function ( array ) { - - var minX = + Infinity; - var minY = + Infinity; - var minZ = + Infinity; - - var maxX = - Infinity; - var maxY = - Infinity; - var maxZ = - Infinity; - - for ( var i = 0, l = array.length; i < l; i += 3 ) { - - var x = array[ i ]; - var y = array[ i + 1 ]; - var z = array[ i + 2 ]; - - if ( x < minX ) { minX = x; } - if ( y < minY ) { minY = y; } - if ( z < minZ ) { minZ = z; } - - if ( x > maxX ) { maxX = x; } - if ( y > maxY ) { maxY = y; } - if ( z > maxZ ) { maxZ = z; } - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - return this; - - }, - - setFromBufferAttribute: function ( attribute ) { - - var minX = + Infinity; - var minY = + Infinity; - var minZ = + Infinity; - - var maxX = - Infinity; - var maxY = - Infinity; - var maxZ = - Infinity; - - for ( var i = 0, l = attribute.count; i < l; i ++ ) { - - var x = attribute.getX( i ); - var y = attribute.getY( i ); - var z = attribute.getZ( i ); - - if ( x < minX ) { minX = x; } - if ( y < minY ) { minY = y; } - if ( z < minZ ) { minZ = z; } - - if ( x > maxX ) { maxX = x; } - if ( y > maxY ) { maxY = y; } - if ( z > maxZ ) { maxZ = z; } - - } - - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); - - return this; - - }, - - setFromPoints: function ( points ) { - - this.makeEmpty(); - - for ( var i = 0, il = points.length; i < il; i ++ ) { - - this.expandByPoint( points[ i ] ); - - } - - return this; - - }, - - setFromCenterAndSize: function ( center, size ) { - - var halfSize = _vector$1.copy( size ).multiplyScalar( 0.5 ); - - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - }, - - setFromObject: function ( object ) { - - this.makeEmpty(); - - return this.expandByObject( object ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); - - return this; - - }, - - makeEmpty: function () { - - this.min.x = this.min.y = this.min.z = + Infinity; - this.max.x = this.max.y = this.max.z = - Infinity; - - return this; - - }, - - isEmpty: function () { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); - - }, - - getCenter: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getCenter() target is now required' ); - target = new Vector3(); - - } - - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - - }, - - getSize: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getSize() target is now required' ); - target = new Vector3(); - - } - - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); - - }, - - expandByPoint: function ( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - }, - - expandByVector: function ( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); - - return this; - - }, - - expandByScalar: function ( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - - return this; - - }, - - expandByObject: function ( object ) { - - // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and children's, world transforms - - object.updateWorldMatrix( false, false ); - - var geometry = object.geometry; - - if ( geometry !== undefined ) { - - if ( geometry.boundingBox === null ) { - - geometry.computeBoundingBox(); - - } - - _box.copy( geometry.boundingBox ); - _box.applyMatrix4( object.matrixWorld ); - - this.union( _box ); - - } - - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - this.expandByObject( children[ i ] ); - - } - - return this; - - }, - - containsPoint: function ( point ) { - - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y || - point.z < this.min.z || point.z > this.max.z ? false : true; - - }, - - containsBox: function ( box ) { - - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y && - this.min.z <= box.min.z && box.max.z <= this.max.z; - - }, - - getParameter: function ( point, target ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getParameter() target is now required' ); - target = new Vector3(); - - } - - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ), - ( point.z - this.min.z ) / ( this.max.z - this.min.z ) - ); - - }, - - intersectsBox: function ( box ) { - - // using 6 splitting planes to rule out intersections. - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y || - box.max.z < this.min.z || box.min.z > this.max.z ? false : true; - - }, - - intersectsSphere: function ( sphere ) { - - // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, _vector$1 ); - - // If that point is inside the sphere, the AABB and sphere intersect. - return _vector$1.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); - - }, - - intersectsPlane: function ( plane ) { - - // We compute the minimum and maximum dot product values. If those values - // are on the same side (back or front) of the plane, then there is no intersection. - - var min, max; - - if ( plane.normal.x > 0 ) { - - min = plane.normal.x * this.min.x; - max = plane.normal.x * this.max.x; - - } else { - - min = plane.normal.x * this.max.x; - max = plane.normal.x * this.min.x; - - } - - if ( plane.normal.y > 0 ) { - - min += plane.normal.y * this.min.y; - max += plane.normal.y * this.max.y; - - } else { - - min += plane.normal.y * this.max.y; - max += plane.normal.y * this.min.y; - - } - - if ( plane.normal.z > 0 ) { - - min += plane.normal.z * this.min.z; - max += plane.normal.z * this.max.z; - - } else { - - min += plane.normal.z * this.max.z; - max += plane.normal.z * this.min.z; - - } - - return ( min <= - plane.constant && max >= - plane.constant ); - - }, - - intersectsTriangle: function ( triangle ) { - - if ( this.isEmpty() ) { - - return false; - - } - - // compute box center and extents - this.getCenter( _center ); - _extents.subVectors( this.max, _center ); - - // translate triangle to aabb origin - _v0.subVectors( triangle.a, _center ); - _v1$2.subVectors( triangle.b, _center ); - _v2.subVectors( triangle.c, _center ); - - // compute edge vectors for triangle - _f0.subVectors( _v1$2, _v0 ); - _f1.subVectors( _v2, _v1$2 ); - _f2.subVectors( _v0, _v2 ); - - // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb - // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation - // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) - var axes = [ - 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, - _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 - ]; - if ( ! satForAxes( axes, _v0, _v1$2, _v2, _extents ) ) { - - return false; - - } - - // test 3 face normals from the aabb - axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; - if ( ! satForAxes( axes, _v0, _v1$2, _v2, _extents ) ) { - - return false; - - } - - // finally testing the face normal of the triangle - // use already existing triangle edge vectors here - _triangleNormal.crossVectors( _f0, _f1 ); - axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; - - return satForAxes( axes, _v0, _v1$2, _v2, _extents ); - - }, - - clampPoint: function ( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .clampPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( point ).clamp( this.min, this.max ); - - }, - - distanceToPoint: function ( point ) { - - var clampedPoint = _vector$1.copy( point ).clamp( this.min, this.max ); - - return clampedPoint.sub( point ).length(); - - }, - - getBoundingSphere: function ( target ) { - - if ( target === undefined ) { - - console.error( 'THREE.Box3: .getBoundingSphere() target is now required' ); - //target = new Sphere(); // removed to avoid cyclic dependency - - } - - this.getCenter( target.center ); - - target.radius = this.getSize( _vector$1 ).length() * 0.5; - - return target; - - }, - - intersect: function ( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. - if ( this.isEmpty() ) { this.makeEmpty(); } - - return this; - - }, - - union: function ( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - }, - - applyMatrix4: function ( matrix ) { - - // transform of empty box is an empty box. - if ( this.isEmpty() ) { return this; } - - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 - _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 - _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 - _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 - _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 - _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 - _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 - _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 - - this.setFromPoints( _points ); - - return this; - - }, - - translate: function ( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - }, - - equals: function ( box ) { - - return box.min.equals( this.min ) && box.max.equals( this.max ); - - } - - } ); - - function satForAxes( axes, v0, v1, v2, extents ) { - - var i, j; - - for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) { - - _testAxis.fromArray( axes, i ); - // project the aabb onto the seperating axis - var r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); - // project all 3 vertices of the triangle onto the seperating axis - var p0 = v0.dot( _testAxis ); - var p1 = v1.dot( _testAxis ); - var p2 = v2.dot( _testAxis ); - // actual test, basically see if either of the most extreme of the triangle points intersects r - if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { - - // points of the projected triangle are outside the projected half-length of the aabb - // the axis is seperating and we can exit - return false; - - } - - } - - return true; - - } - - var _box$1 = new Box3(); - - /** - * @author bhouston / http://clara.io - * @author mrdoob / http://mrdoob.com/ - */ - - function Sphere( center, radius ) { - - this.center = ( center !== undefined ) ? center : new Vector3(); - this.radius = ( radius !== undefined ) ? radius : - 1; - - } - - Object.assign( Sphere.prototype, { - - set: function ( center, radius ) { - - this.center.copy( center ); - this.radius = radius; - - return this; - - }, - - setFromPoints: function ( points, optionalCenter ) { - - var center = this.center; - - if ( optionalCenter !== undefined ) { - - center.copy( optionalCenter ); - - } else { - - _box$1.setFromPoints( points ).getCenter( center ); - - } - - var maxRadiusSq = 0; - - for ( var i = 0, il = points.length; i < il; i ++ ) { - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); - - } - - this.radius = Math.sqrt( maxRadiusSq ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( sphere ) { - - this.center.copy( sphere.center ); - this.radius = sphere.radius; - - return this; - - }, - - isEmpty: function () { - - return ( this.radius < 0 ); - - }, - - makeEmpty: function () { - - this.center.set( 0, 0, 0 ); - this.radius = - 1; - - return this; - - }, - - containsPoint: function ( point ) { - - return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - - }, - - distanceToPoint: function ( point ) { - - return ( point.distanceTo( this.center ) - this.radius ); - - }, - - intersectsSphere: function ( sphere ) { - - var radiusSum = this.radius + sphere.radius; - - return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); - - }, - - intersectsBox: function ( box ) { - - return box.intersectsSphere( this ); - - }, - - intersectsPlane: function ( plane ) { - - return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; - - }, - - clampPoint: function ( point, target ) { - - var deltaLengthSq = this.center.distanceToSquared( point ); - - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .clampPoint() target is now required' ); - target = new Vector3(); - - } - - target.copy( point ); - - if ( deltaLengthSq > ( this.radius * this.radius ) ) { - - target.sub( this.center ).normalize(); - target.multiplyScalar( this.radius ).add( this.center ); - - } - - return target; - - }, - - getBoundingBox: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' ); - target = new Box3(); - - } - - if ( this.isEmpty() ) { - - // Empty sphere produces empty bounding box - target.makeEmpty(); - return target; - - } - - target.set( this.center, this.center ); - target.expandByScalar( this.radius ); - - return target; - - }, - - applyMatrix4: function ( matrix ) { - - this.center.applyMatrix4( matrix ); - this.radius = this.radius * matrix.getMaxScaleOnAxis(); - - return this; - - }, - - translate: function ( offset ) { - - this.center.add( offset ); - - return this; - - }, - - equals: function ( sphere ) { - - return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); - - } - - } ); - - var _vector$2 = new Vector3(); - var _segCenter = new Vector3(); - var _segDir = new Vector3(); - var _diff = new Vector3(); - - var _edge1 = new Vector3(); - var _edge2 = new Vector3(); - var _normal = new Vector3(); - - /** - * @author bhouston / http://clara.io - */ - - function Ray( origin, direction ) { - - this.origin = ( origin !== undefined ) ? origin : new Vector3(); - this.direction = ( direction !== undefined ) ? direction : new Vector3( 0, 0, - 1 ); - - } - - Object.assign( Ray.prototype, { - - set: function ( origin, direction ) { - - this.origin.copy( origin ); - this.direction.copy( direction ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( ray ) { - - this.origin.copy( ray.origin ); - this.direction.copy( ray.direction ); - - return this; - - }, - - at: function ( t, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .at() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); - - }, - - lookAt: function ( v ) { - - this.direction.copy( v ).sub( this.origin ).normalize(); - - return this; - - }, - - recast: function ( t ) { - - this.origin.copy( this.at( t, _vector$2 ) ); - - return this; - - }, - - closestPointToPoint: function ( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - target.subVectors( point, this.origin ); - - var directionDistance = target.dot( this.direction ); - - if ( directionDistance < 0 ) { - - return target.copy( this.origin ); - - } - - return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - }, - - distanceToPoint: function ( point ) { - - return Math.sqrt( this.distanceSqToPoint( point ) ); - - }, - - distanceSqToPoint: function ( point ) { - - var directionDistance = _vector$2.subVectors( point, this.origin ).dot( this.direction ); - - // point behind the ray - - if ( directionDistance < 0 ) { - - return this.origin.distanceToSquared( point ); - - } - - _vector$2.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - - return _vector$2.distanceToSquared( point ); - - }, - - distanceSqToSegment: function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h - // It returns the min distance between the ray and the segment - // defined by v0 and v1 - // It can also set two optional targets : - // - The closest point on the ray - // - The closest point on the segment - - _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); - _segDir.copy( v1 ).sub( v0 ).normalize(); - _diff.copy( this.origin ).sub( _segCenter ); - - var segExtent = v0.distanceTo( v1 ) * 0.5; - var a01 = - this.direction.dot( _segDir ); - var b0 = _diff.dot( this.direction ); - var b1 = - _diff.dot( _segDir ); - var c = _diff.lengthSq(); - var det = Math.abs( 1 - a01 * a01 ); - var s0, s1, sqrDist, extDet; - - if ( det > 0 ) { - - // The ray and segment are not parallel. - - s0 = a01 * b1 - b0; - s1 = a01 * b0 - b1; - extDet = segExtent * det; - - if ( s0 >= 0 ) { - - if ( s1 >= - extDet ) { - - if ( s1 <= extDet ) { - - // region 0 - // Minimum at interior points of ray and segment. - - var invDet = 1 / det; - s0 *= invDet; - s1 *= invDet; - sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; - - } else { - - // region 1 - - s1 = segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - // region 5 - - s1 = - segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } else { - - if ( s1 <= - extDet ) { - - // region 4 - - s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } else if ( s1 <= extDet ) { - - // region 3 - - s0 = 0; - s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = s1 * ( s1 + 2 * b1 ) + c; - - } else { - - // region 2 - - s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - } - - } else { - - // Ray and segment are parallel. - - s1 = ( a01 > 0 ) ? - segExtent : segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } - - if ( optionalPointOnRay ) { - - optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); - - } - - if ( optionalPointOnSegment ) { - - optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); - - } - - return sqrDist; - - }, - - intersectSphere: function ( sphere, target ) { - - _vector$2.subVectors( sphere.center, this.origin ); - var tca = _vector$2.dot( this.direction ); - var d2 = _vector$2.dot( _vector$2 ) - tca * tca; - var radius2 = sphere.radius * sphere.radius; - - if ( d2 > radius2 ) { return null; } - - var thc = Math.sqrt( radius2 - d2 ); - - // t0 = first intersect point - entrance on front of sphere - var t0 = tca - thc; - - // t1 = second intersect point - exit point on back of sphere - var t1 = tca + thc; - - // test to see if both t0 and t1 are behind the ray - if so, return null - if ( t0 < 0 && t1 < 0 ) { return null; } - - // test to see if t0 is behind the ray: - // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, - // in order to always return an intersect point that is in front of the ray. - if ( t0 < 0 ) { return this.at( t1, target ); } - - // else t0 is in front of the ray, so return the first collision point scaled by t0 - return this.at( t0, target ); - - }, - - intersectsSphere: function ( sphere ) { - - return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); - - }, - - distanceToPlane: function ( plane ) { - - var denominator = plane.normal.dot( this.direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) === 0 ) { - - return 0; - - } - - // Null is preferable to undefined since undefined means.... it is undefined - - return null; - - } - - var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; - - // Return if the ray never intersects the plane - - return t >= 0 ? t : null; - - }, - - intersectPlane: function ( plane, target ) { - - var t = this.distanceToPlane( plane ); - - if ( t === null ) { - - return null; - - } - - return this.at( t, target ); - - }, - - intersectsPlane: function ( plane ) { - - // check if the ray lies on the plane first - - var distToPoint = plane.distanceToPoint( this.origin ); - - if ( distToPoint === 0 ) { - - return true; - - } - - var denominator = plane.normal.dot( this.direction ); - - if ( denominator * distToPoint < 0 ) { - - return true; - - } - - // ray origin is behind the plane (and is pointing behind it) - - return false; - - }, - - intersectBox: function ( box, target ) { - - var tmin, tmax, tymin, tymax, tzmin, tzmax; - - var invdirx = 1 / this.direction.x, - invdiry = 1 / this.direction.y, - invdirz = 1 / this.direction.z; - - var origin = this.origin; - - if ( invdirx >= 0 ) { - - tmin = ( box.min.x - origin.x ) * invdirx; - tmax = ( box.max.x - origin.x ) * invdirx; - - } else { - - tmin = ( box.max.x - origin.x ) * invdirx; - tmax = ( box.min.x - origin.x ) * invdirx; - - } - - if ( invdiry >= 0 ) { - - tymin = ( box.min.y - origin.y ) * invdiry; - tymax = ( box.max.y - origin.y ) * invdiry; - - } else { - - tymin = ( box.max.y - origin.y ) * invdiry; - tymax = ( box.min.y - origin.y ) * invdiry; - - } - - if ( ( tmin > tymax ) || ( tymin > tmax ) ) { return null; } - - // These lines also handle the case where tmin or tmax is NaN - // (result of 0 * Infinity). x !== x returns true if x is NaN - - if ( tymin > tmin || tmin !== tmin ) { tmin = tymin; } - - if ( tymax < tmax || tmax !== tmax ) { tmax = tymax; } - - if ( invdirz >= 0 ) { - - tzmin = ( box.min.z - origin.z ) * invdirz; - tzmax = ( box.max.z - origin.z ) * invdirz; - - } else { - - tzmin = ( box.max.z - origin.z ) * invdirz; - tzmax = ( box.min.z - origin.z ) * invdirz; - - } - - if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) { return null; } - - if ( tzmin > tmin || tmin !== tmin ) { tmin = tzmin; } - - if ( tzmax < tmax || tmax !== tmax ) { tmax = tzmax; } - - //return point closest to the ray (positive side) - - if ( tmax < 0 ) { return null; } - - return this.at( tmin >= 0 ? tmin : tmax, target ); - - }, - - intersectsBox: function ( box ) { - - return this.intersectBox( box, _vector$2 ) !== null; - - }, - - intersectTriangle: function ( a, b, c, backfaceCulling, target ) { - - // Compute the offset origin, edges, and normal. - - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - - _edge1.subVectors( b, a ); - _edge2.subVectors( c, a ); - _normal.crossVectors( _edge1, _edge2 ); - - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - var DdN = this.direction.dot( _normal ); - var sign; - - if ( DdN > 0 ) { - - if ( backfaceCulling ) { return null; } - sign = 1; - - } else if ( DdN < 0 ) { - - sign = - 1; - DdN = - DdN; - - } else { - - return null; - - } - - _diff.subVectors( this.origin, a ); - var DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); - - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { - - return null; - - } - - var DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); - - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { - - return null; - - } - - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { - - return null; - - } - - // Line intersects triangle, check if ray does. - var QdN = - sign * _diff.dot( _normal ); - - // t < 0, no intersection - if ( QdN < 0 ) { - - return null; - - } - - // Ray intersects triangle. - return this.at( QdN / DdN, target ); - - }, - - applyMatrix4: function ( matrix4 ) { - - this.origin.applyMatrix4( matrix4 ); - this.direction.transformDirection( matrix4 ); - - return this; - - }, - - equals: function ( ray ) { - - return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); - - } - - } ); - - /** - * @author bhouston / http://clara.io - */ - - var _vector1 = new Vector3(); - var _vector2 = new Vector3(); - var _normalMatrix = new Matrix3(); - - function Plane( normal, constant ) { - - // normal is assumed to be normalized - - this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); - this.constant = ( constant !== undefined ) ? constant : 0; - - } - - Object.assign( Plane.prototype, { - - isPlane: true, - - set: function ( normal, constant ) { - - this.normal.copy( normal ); - this.constant = constant; - - return this; - - }, - - setComponents: function ( x, y, z, w ) { - - this.normal.set( x, y, z ); - this.constant = w; - - return this; - - }, - - setFromNormalAndCoplanarPoint: function ( normal, point ) { - - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); - - return this; - - }, - - setFromCoplanarPoints: function ( a, b, c ) { - - var normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); - - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? - - this.setFromNormalAndCoplanarPoint( normal, a ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( plane ) { - - this.normal.copy( plane.normal ); - this.constant = plane.constant; - - return this; - - }, - - normalize: function () { - - // Note: will lead to a divide by zero if the plane is invalid. - - var inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; - - return this; - - }, - - negate: function () { - - this.constant *= - 1; - this.normal.negate(); - - return this; - - }, - - distanceToPoint: function ( point ) { - - return this.normal.dot( point ) + this.constant; - - }, - - distanceToSphere: function ( sphere ) { - - return this.distanceToPoint( sphere.center ) - sphere.radius; - - }, - - projectPoint: function ( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .projectPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); - - }, - - intersectLine: function ( line, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .intersectLine() target is now required' ); - target = new Vector3(); - - } - - var direction = line.delta( _vector1 ); - - var denominator = this.normal.dot( direction ); - - if ( denominator === 0 ) { - - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { - - return target.copy( line.start ); - - } - - // Unsure if this is the correct method to handle this case. - return undefined; - - } - - var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; - - if ( t < 0 || t > 1 ) { - - return undefined; - - } - - return target.copy( direction ).multiplyScalar( t ).add( line.start ); - - }, - - intersectsLine: function ( line ) { - - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - - var startSign = this.distanceToPoint( line.start ); - var endSign = this.distanceToPoint( line.end ); - - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); - - }, - - intersectsBox: function ( box ) { - - return box.intersectsPlane( this ); - - }, - - intersectsSphere: function ( sphere ) { - - return sphere.intersectsPlane( this ); - - }, - - coplanarPoint: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .coplanarPoint() target is now required' ); - target = new Vector3(); - - } - - return target.copy( this.normal ).multiplyScalar( - this.constant ); - - }, - - applyMatrix4: function ( matrix, optionalNormalMatrix ) { - - var normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); - - var referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); - - var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); - - this.constant = - referencePoint.dot( normal ); - - return this; - - }, - - translate: function ( offset ) { - - this.constant -= offset.dot( this.normal ); - - return this; - - }, - - equals: function ( plane ) { - - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); - - } - - } ); - - /** - * @author bhouston / http://clara.io - * @author mrdoob / http://mrdoob.com/ - */ - - var _v0$1 = new Vector3(); - var _v1$3 = new Vector3(); - var _v2$1 = new Vector3(); - var _v3 = new Vector3(); - - var _vab = new Vector3(); - var _vac = new Vector3(); - var _vbc = new Vector3(); - var _vap = new Vector3(); - var _vbp = new Vector3(); - var _vcp = new Vector3(); - - function Triangle( a, b, c ) { - - this.a = ( a !== undefined ) ? a : new Vector3(); - this.b = ( b !== undefined ) ? b : new Vector3(); - this.c = ( c !== undefined ) ? c : new Vector3(); - - } - - Object.assign( Triangle, { - - getNormal: function ( a, b, c, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getNormal() target is now required' ); - target = new Vector3(); - - } - - target.subVectors( c, b ); - _v0$1.subVectors( a, b ); - target.cross( _v0$1 ); - - var targetLengthSq = target.lengthSq(); - if ( targetLengthSq > 0 ) { - - return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); - - } - - return target.set( 0, 0, 0 ); - - }, - - // static/instance method to calculate barycentric coordinates - // based on: http://www.blackpawn.com/texts/pointinpoly/default.html - getBarycoord: function ( point, a, b, c, target ) { - - _v0$1.subVectors( c, a ); - _v1$3.subVectors( b, a ); - _v2$1.subVectors( point, a ); - - var dot00 = _v0$1.dot( _v0$1 ); - var dot01 = _v0$1.dot( _v1$3 ); - var dot02 = _v0$1.dot( _v2$1 ); - var dot11 = _v1$3.dot( _v1$3 ); - var dot12 = _v1$3.dot( _v2$1 ); - - var denom = ( dot00 * dot11 - dot01 * dot01 ); - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getBarycoord() target is now required' ); - target = new Vector3(); - - } - - // collinear or singular triangle - if ( denom === 0 ) { - - // arbitrary location outside of triangle? - // not sure if this is the best idea, maybe should be returning undefined - return target.set( - 2, - 1, - 1 ); - - } - - var invDenom = 1 / denom; - var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; - var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - - // barycentric coordinates must always sum to 1 - return target.set( 1 - u - v, v, u ); - - }, - - containsPoint: function ( point, a, b, c ) { - - Triangle.getBarycoord( point, a, b, c, _v3 ); - - return ( _v3.x >= 0 ) && ( _v3.y >= 0 ) && ( ( _v3.x + _v3.y ) <= 1 ); - - }, - - getUV: function ( point, p1, p2, p3, uv1, uv2, uv3, target ) { - - this.getBarycoord( point, p1, p2, p3, _v3 ); - - target.set( 0, 0 ); - target.addScaledVector( uv1, _v3.x ); - target.addScaledVector( uv2, _v3.y ); - target.addScaledVector( uv3, _v3.z ); - - return target; - - }, - - isFrontFacing: function ( a, b, c, direction ) { - - _v0$1.subVectors( c, b ); - _v1$3.subVectors( a, b ); - - // strictly front facing - return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; - - } - - } ); - - Object.assign( Triangle.prototype, { - - set: function ( a, b, c ) { - - this.a.copy( a ); - this.b.copy( b ); - this.c.copy( c ); - - return this; - - }, - - setFromPointsAndIndices: function ( points, i0, i1, i2 ) { - - this.a.copy( points[ i0 ] ); - this.b.copy( points[ i1 ] ); - this.c.copy( points[ i2 ] ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( triangle ) { - - this.a.copy( triangle.a ); - this.b.copy( triangle.b ); - this.c.copy( triangle.c ); - - return this; - - }, - - getArea: function () { - - _v0$1.subVectors( this.c, this.b ); - _v1$3.subVectors( this.a, this.b ); - - return _v0$1.cross( _v1$3 ).length() * 0.5; - - }, - - getMidpoint: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getMidpoint() target is now required' ); - target = new Vector3(); - - } - - return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); - - }, - - getNormal: function ( target ) { - - return Triangle.getNormal( this.a, this.b, this.c, target ); - - }, - - getPlane: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getPlane() target is now required' ); - target = new Plane(); - - } - - return target.setFromCoplanarPoints( this.a, this.b, this.c ); - - }, - - getBarycoord: function ( point, target ) { - - return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); - - }, - - getUV: function ( point, uv1, uv2, uv3, target ) { - - return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); - - }, - - containsPoint: function ( point ) { - - return Triangle.containsPoint( point, this.a, this.b, this.c ); - - }, - - isFrontFacing: function ( direction ) { - - return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); - - }, - - intersectsBox: function ( box ) { - - return box.intersectsTriangle( this ); - - }, - - closestPointToPoint: function ( p, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - var a = this.a, b = this.b, c = this.c; - var v, w; - - // algorithm thanks to Real-Time Collision Detection by Christer Ericson, - // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., - // under the accompanying license; see chapter 5.1.5 for detailed explanation. - // basically, we're distinguishing which of the voronoi regions of the triangle - // the point lies in with the minimum amount of redundant computation. - - _vab.subVectors( b, a ); - _vac.subVectors( c, a ); - _vap.subVectors( p, a ); - var d1 = _vab.dot( _vap ); - var d2 = _vac.dot( _vap ); - if ( d1 <= 0 && d2 <= 0 ) { - - // vertex region of A; barycentric coords (1, 0, 0) - return target.copy( a ); - - } - - _vbp.subVectors( p, b ); - var d3 = _vab.dot( _vbp ); - var d4 = _vac.dot( _vbp ); - if ( d3 >= 0 && d4 <= d3 ) { - - // vertex region of B; barycentric coords (0, 1, 0) - return target.copy( b ); - - } - - var vc = d1 * d4 - d3 * d2; - if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { - - v = d1 / ( d1 - d3 ); - // edge region of AB; barycentric coords (1-v, v, 0) - return target.copy( a ).addScaledVector( _vab, v ); - - } - - _vcp.subVectors( p, c ); - var d5 = _vab.dot( _vcp ); - var d6 = _vac.dot( _vcp ); - if ( d6 >= 0 && d5 <= d6 ) { - - // vertex region of C; barycentric coords (0, 0, 1) - return target.copy( c ); - - } - - var vb = d5 * d2 - d1 * d6; - if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { - - w = d2 / ( d2 - d6 ); - // edge region of AC; barycentric coords (1-w, 0, w) - return target.copy( a ).addScaledVector( _vac, w ); - - } - - var va = d3 * d6 - d5 * d4; - if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { - - _vbc.subVectors( c, b ); - w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); - // edge region of BC; barycentric coords (0, 1-w, w) - return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC - - } - - // face region - var denom = 1 / ( va + vb + vc ); - // u = va * denom - v = vb * denom; - w = vc * denom; - - return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); - - }, - - equals: function ( triangle ) { - - return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, - 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, - 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, - 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, - 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, - 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, - 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, - 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, - 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, - 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, - 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, - 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, - 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, - 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, - 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, - 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, - 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, - 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, - 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, - 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, - 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, - 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, - 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, - 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; - - var _hslA = { h: 0, s: 0, l: 0 }; - var _hslB = { h: 0, s: 0, l: 0 }; - - function Color( r, g, b ) { - - if ( g === undefined && b === undefined ) { - - // r is THREE.Color, hex or string - return this.set( r ); - - } - - return this.setRGB( r, g, b ); - - } - - function hue2rgb( p, q, t ) { - - if ( t < 0 ) { t += 1; } - if ( t > 1 ) { t -= 1; } - if ( t < 1 / 6 ) { return p + ( q - p ) * 6 * t; } - if ( t < 1 / 2 ) { return q; } - if ( t < 2 / 3 ) { return p + ( q - p ) * 6 * ( 2 / 3 - t ); } - return p; - - } - - function SRGBToLinear( c ) { - - return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); - - } - - function LinearToSRGB( c ) { - - return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; - - } - - Object.assign( Color.prototype, { - - isColor: true, - - r: 1, g: 1, b: 1, - - set: function ( value ) { - - if ( value && value.isColor ) { - - this.copy( value ); - - } else if ( typeof value === 'number' ) { - - this.setHex( value ); - - } else if ( typeof value === 'string' ) { - - this.setStyle( value ); - - } - - return this; - - }, - - setScalar: function ( scalar ) { - - this.r = scalar; - this.g = scalar; - this.b = scalar; - - return this; - - }, - - setHex: function ( hex ) { - - hex = Math.floor( hex ); - - this.r = ( hex >> 16 & 255 ) / 255; - this.g = ( hex >> 8 & 255 ) / 255; - this.b = ( hex & 255 ) / 255; - - return this; - - }, - - setRGB: function ( r, g, b ) { - - this.r = r; - this.g = g; - this.b = b; - - return this; - - }, - - setHSL: function ( h, s, l ) { - - // h,s,l ranges are in 0.0 - 1.0 - h = MathUtils.euclideanModulo( h, 1 ); - s = MathUtils.clamp( s, 0, 1 ); - l = MathUtils.clamp( l, 0, 1 ); - - if ( s === 0 ) { - - this.r = this.g = this.b = l; - - } else { - - var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); - var q = ( 2 * l ) - p; - - this.r = hue2rgb( q, p, h + 1 / 3 ); - this.g = hue2rgb( q, p, h ); - this.b = hue2rgb( q, p, h - 1 / 3 ); - - } - - return this; - - }, - - setStyle: function ( style ) { - - function handleAlpha( string ) { - - if ( string === undefined ) { return; } - - if ( parseFloat( string ) < 1 ) { - - console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); - - } - - } - - - var m; - - if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { - - // rgb / hsl - - var color; - var name = m[ 1 ]; - var components = m[ 2 ]; - - switch ( name ) { - - case 'rgb': - case 'rgba': - - if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // rgb(255,0,0) rgba(255,0,0,0.5) - this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; - this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; - this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; - - handleAlpha( color[ 5 ] ); - - return this; - - } - - if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) - this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; - this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; - this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; - - handleAlpha( color[ 5 ] ); - - return this; - - } - - break; - - case 'hsl': - case 'hsla': - - if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { - - // hsl(120,50%,50%) hsla(120,50%,50%,0.5) - var h = parseFloat( color[ 1 ] ) / 360; - var s = parseInt( color[ 2 ], 10 ) / 100; - var l = parseInt( color[ 3 ], 10 ) / 100; - - handleAlpha( color[ 5 ] ); - - return this.setHSL( h, s, l ); - - } - - break; - - } - - } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { - - // hex color - - var hex = m[ 1 ]; - var size = hex.length; - - if ( size === 3 ) { - - // #ff0 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; - - return this; - - } else if ( size === 6 ) { - - // #ff0000 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; - - return this; - - } - - } - - if ( style && style.length > 0 ) { - - return this.setColorName( style ); - - } - - return this; - - }, - - setColorName: function ( style ) { - - // color keywords - var hex = _colorKeywords[ style ]; - - if ( hex !== undefined ) { - - // red - this.setHex( hex ); - - } else { - - // unknown color - console.warn( 'THREE.Color: Unknown color ' + style ); - - } - - return this; - - }, - - clone: function () { - - return new this.constructor( this.r, this.g, this.b ); - - }, - - copy: function ( color ) { - - this.r = color.r; - this.g = color.g; - this.b = color.b; - - return this; - - }, - - copyGammaToLinear: function ( color, gammaFactor ) { - - if ( gammaFactor === undefined ) { gammaFactor = 2.0; } - - this.r = Math.pow( color.r, gammaFactor ); - this.g = Math.pow( color.g, gammaFactor ); - this.b = Math.pow( color.b, gammaFactor ); - - return this; - - }, - - copyLinearToGamma: function ( color, gammaFactor ) { - - if ( gammaFactor === undefined ) { gammaFactor = 2.0; } - - var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; - - this.r = Math.pow( color.r, safeInverse ); - this.g = Math.pow( color.g, safeInverse ); - this.b = Math.pow( color.b, safeInverse ); - - return this; - - }, - - convertGammaToLinear: function ( gammaFactor ) { - - this.copyGammaToLinear( this, gammaFactor ); - - return this; - - }, - - convertLinearToGamma: function ( gammaFactor ) { - - this.copyLinearToGamma( this, gammaFactor ); - - return this; - - }, - - copySRGBToLinear: function ( color ) { - - this.r = SRGBToLinear( color.r ); - this.g = SRGBToLinear( color.g ); - this.b = SRGBToLinear( color.b ); - - return this; - - }, - - copyLinearToSRGB: function ( color ) { - - this.r = LinearToSRGB( color.r ); - this.g = LinearToSRGB( color.g ); - this.b = LinearToSRGB( color.b ); - - return this; - - }, - - convertSRGBToLinear: function () { - - this.copySRGBToLinear( this ); - - return this; - - }, - - convertLinearToSRGB: function () { - - this.copyLinearToSRGB( this ); - - return this; - - }, - - getHex: function () { - - return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; - - }, - - getHexString: function () { - - return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); - - }, - - getHSL: function ( target ) { - - // h,s,l ranges are in 0.0 - 1.0 - - if ( target === undefined ) { - - console.warn( 'THREE.Color: .getHSL() target is now required' ); - target = { h: 0, s: 0, l: 0 }; - - } - - var r = this.r, g = this.g, b = this.b; - - var max = Math.max( r, g, b ); - var min = Math.min( r, g, b ); - - var hue, saturation; - var lightness = ( min + max ) / 2.0; - - if ( min === max ) { - - hue = 0; - saturation = 0; - - } else { - - var delta = max - min; - - saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); - - switch ( max ) { - - case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; - case g: hue = ( b - r ) / delta + 2; break; - case b: hue = ( r - g ) / delta + 4; break; - - } - - hue /= 6; - - } - - target.h = hue; - target.s = saturation; - target.l = lightness; - - return target; - - }, - - getStyle: function () { - - return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; - - }, - - offsetHSL: function ( h, s, l ) { - - this.getHSL( _hslA ); - - _hslA.h += h; _hslA.s += s; _hslA.l += l; - - this.setHSL( _hslA.h, _hslA.s, _hslA.l ); - - return this; - - }, - - add: function ( color ) { - - this.r += color.r; - this.g += color.g; - this.b += color.b; - - return this; - - }, - - addColors: function ( color1, color2 ) { - - this.r = color1.r + color2.r; - this.g = color1.g + color2.g; - this.b = color1.b + color2.b; - - return this; - - }, - - addScalar: function ( s ) { - - this.r += s; - this.g += s; - this.b += s; - - return this; - - }, - - sub: function ( color ) { - - this.r = Math.max( 0, this.r - color.r ); - this.g = Math.max( 0, this.g - color.g ); - this.b = Math.max( 0, this.b - color.b ); - - return this; - - }, - - multiply: function ( color ) { - - this.r *= color.r; - this.g *= color.g; - this.b *= color.b; - - return this; - - }, - - multiplyScalar: function ( s ) { - - this.r *= s; - this.g *= s; - this.b *= s; - - return this; - - }, - - lerp: function ( color, alpha ) { - - this.r += ( color.r - this.r ) * alpha; - this.g += ( color.g - this.g ) * alpha; - this.b += ( color.b - this.b ) * alpha; - - return this; - - }, - - lerpHSL: function ( color, alpha ) { - - this.getHSL( _hslA ); - color.getHSL( _hslB ); - - var h = MathUtils.lerp( _hslA.h, _hslB.h, alpha ); - var s = MathUtils.lerp( _hslA.s, _hslB.s, alpha ); - var l = MathUtils.lerp( _hslA.l, _hslB.l, alpha ); - - this.setHSL( h, s, l ); - - return this; - - }, - - equals: function ( c ) { - - return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) { offset = 0; } - - this.r = array[ offset ]; - this.g = array[ offset + 1 ]; - this.b = array[ offset + 2 ]; - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) { array = []; } - if ( offset === undefined ) { offset = 0; } - - array[ offset ] = this.r; - array[ offset + 1 ] = this.g; - array[ offset + 2 ] = this.b; - - return array; - - }, - - toJSON: function () { - - return this.getHex(); - - } - - } ); - - Color.NAMES = _colorKeywords; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function Face3( a, b, c, normal, color, materialIndex ) { - - this.a = a; - this.b = b; - this.c = c; - - this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); - this.vertexNormals = Array.isArray( normal ) ? normal : []; - - this.color = ( color && color.isColor ) ? color : new Color(); - this.vertexColors = Array.isArray( color ) ? color : []; - - this.materialIndex = materialIndex !== undefined ? materialIndex : 0; - - } - - Object.assign( Face3.prototype, { - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.a = source.a; - this.b = source.b; - this.c = source.c; - - this.normal.copy( source.normal ); - this.color.copy( source.color ); - - this.materialIndex = source.materialIndex; - - for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { - - this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); - - } - - for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { - - this.vertexColors[ i ] = source.vertexColors[ i ].clone(); - - } - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - var materialId = 0; - - function Material() { - - Object.defineProperty( this, 'id', { value: materialId ++ } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'Material'; - - this.fog = true; - - this.blending = NormalBlending; - this.side = FrontSide; - this.flatShading = false; - this.vertexColors = false; - - this.opacity = 1; - this.transparent = false; - - this.blendSrc = SrcAlphaFactor; - this.blendDst = OneMinusSrcAlphaFactor; - this.blendEquation = AddEquation; - this.blendSrcAlpha = null; - this.blendDstAlpha = null; - this.blendEquationAlpha = null; - - this.depthFunc = LessEqualDepth; - this.depthTest = true; - this.depthWrite = true; - - this.stencilWriteMask = 0xff; - this.stencilFunc = AlwaysStencilFunc; - this.stencilRef = 0; - this.stencilFuncMask = 0xff; - this.stencilFail = KeepStencilOp; - this.stencilZFail = KeepStencilOp; - this.stencilZPass = KeepStencilOp; - this.stencilWrite = false; - - this.clippingPlanes = null; - this.clipIntersection = false; - this.clipShadows = false; - - this.shadowSide = null; - - this.colorWrite = true; - - this.precision = null; // override the renderer's default precision for this material - - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; - - this.dithering = false; - - this.alphaTest = 0; - this.premultipliedAlpha = false; - - this.visible = true; - - this.toneMapped = true; - - this.userData = {}; - - this.version = 0; - - } - - Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Material, - - isMaterial: true, - - onBeforeCompile: function () {}, - - setValues: function ( values ) { - - if ( values === undefined ) { return; } - - for ( var key in values ) { - - var newValue = values[ key ]; - - if ( newValue === undefined ) { - - console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); - continue; - - } - - // for backward compatability if shading is set in the constructor - if ( key === 'shading' ) { - - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( newValue === FlatShading ) ? true : false; - continue; - - } - - var currentValue = this[ key ]; - - if ( currentValue === undefined ) { - - console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); - continue; - - } - - if ( currentValue && currentValue.isColor ) { - - currentValue.set( newValue ); - - } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { - - currentValue.copy( newValue ); - - } else { - - this[ key ] = newValue; - - } - - } - - }, - - toJSON: function ( meta ) { - - var isRoot = ( meta === undefined || typeof meta === 'string' ); - - if ( isRoot ) { - - meta = { - textures: {}, - images: {} - }; - - } - - var data = { - metadata: { - version: 4.5, - type: 'Material', - generator: 'Material.toJSON' - } - }; - - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; - - if ( this.name !== '' ) { data.name = this.name; } - - if ( this.color && this.color.isColor ) { data.color = this.color.getHex(); } - - if ( this.roughness !== undefined ) { data.roughness = this.roughness; } - if ( this.metalness !== undefined ) { data.metalness = this.metalness; } - - if ( this.sheen && this.sheen.isColor ) { data.sheen = this.sheen.getHex(); } - if ( this.emissive && this.emissive.isColor ) { data.emissive = this.emissive.getHex(); } - if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) { data.emissiveIntensity = this.emissiveIntensity; } - - if ( this.specular && this.specular.isColor ) { data.specular = this.specular.getHex(); } - if ( this.shininess !== undefined ) { data.shininess = this.shininess; } - if ( this.clearcoat !== undefined ) { data.clearcoat = this.clearcoat; } - if ( this.clearcoatRoughness !== undefined ) { data.clearcoatRoughness = this.clearcoatRoughness; } - - if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { - - data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { - - data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; - - } - - if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { - - data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; - data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); - - } - - if ( this.map && this.map.isTexture ) { data.map = this.map.toJSON( meta ).uuid; } - if ( this.matcap && this.matcap.isTexture ) { data.matcap = this.matcap.toJSON( meta ).uuid; } - if ( this.alphaMap && this.alphaMap.isTexture ) { data.alphaMap = this.alphaMap.toJSON( meta ).uuid; } - if ( this.lightMap && this.lightMap.isTexture ) { data.lightMap = this.lightMap.toJSON( meta ).uuid; } - - if ( this.aoMap && this.aoMap.isTexture ) { - - data.aoMap = this.aoMap.toJSON( meta ).uuid; - data.aoMapIntensity = this.aoMapIntensity; - - } - - if ( this.bumpMap && this.bumpMap.isTexture ) { - - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; - - } - - if ( this.normalMap && this.normalMap.isTexture ) { - - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalMapType = this.normalMapType; - data.normalScale = this.normalScale.toArray(); - - } - - if ( this.displacementMap && this.displacementMap.isTexture ) { - - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; - - } - - if ( this.roughnessMap && this.roughnessMap.isTexture ) { data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; } - if ( this.metalnessMap && this.metalnessMap.isTexture ) { data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; } - - if ( this.emissiveMap && this.emissiveMap.isTexture ) { data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; } - if ( this.specularMap && this.specularMap.isTexture ) { data.specularMap = this.specularMap.toJSON( meta ).uuid; } - - if ( this.envMap && this.envMap.isTexture ) { - - data.envMap = this.envMap.toJSON( meta ).uuid; - data.reflectivity = this.reflectivity; // Scale behind envMap - data.refractionRatio = this.refractionRatio; - - if ( this.combine !== undefined ) { data.combine = this.combine; } - if ( this.envMapIntensity !== undefined ) { data.envMapIntensity = this.envMapIntensity; } - - } - - if ( this.gradientMap && this.gradientMap.isTexture ) { - - data.gradientMap = this.gradientMap.toJSON( meta ).uuid; - - } - - if ( this.size !== undefined ) { data.size = this.size; } - if ( this.sizeAttenuation !== undefined ) { data.sizeAttenuation = this.sizeAttenuation; } - - if ( this.blending !== NormalBlending ) { data.blending = this.blending; } - if ( this.flatShading === true ) { data.flatShading = this.flatShading; } - if ( this.side !== FrontSide ) { data.side = this.side; } - if ( this.vertexColors ) { data.vertexColors = true; } - - if ( this.opacity < 1 ) { data.opacity = this.opacity; } - if ( this.transparent === true ) { data.transparent = this.transparent; } - - data.depthFunc = this.depthFunc; - data.depthTest = this.depthTest; - data.depthWrite = this.depthWrite; - - data.stencilWrite = this.stencilWrite; - data.stencilWriteMask = this.stencilWriteMask; - data.stencilFunc = this.stencilFunc; - data.stencilRef = this.stencilRef; - data.stencilFuncMask = this.stencilFuncMask; - data.stencilFail = this.stencilFail; - data.stencilZFail = this.stencilZFail; - data.stencilZPass = this.stencilZPass; - - // rotation (SpriteMaterial) - if ( this.rotation && this.rotation !== 0 ) { data.rotation = this.rotation; } - - if ( this.polygonOffset === true ) { data.polygonOffset = true; } - if ( this.polygonOffsetFactor !== 0 ) { data.polygonOffsetFactor = this.polygonOffsetFactor; } - if ( this.polygonOffsetUnits !== 0 ) { data.polygonOffsetUnits = this.polygonOffsetUnits; } - - if ( this.linewidth && this.linewidth !== 1 ) { data.linewidth = this.linewidth; } - if ( this.dashSize !== undefined ) { data.dashSize = this.dashSize; } - if ( this.gapSize !== undefined ) { data.gapSize = this.gapSize; } - if ( this.scale !== undefined ) { data.scale = this.scale; } - - if ( this.dithering === true ) { data.dithering = true; } - - if ( this.alphaTest > 0 ) { data.alphaTest = this.alphaTest; } - if ( this.premultipliedAlpha === true ) { data.premultipliedAlpha = this.premultipliedAlpha; } - - if ( this.wireframe === true ) { data.wireframe = this.wireframe; } - if ( this.wireframeLinewidth > 1 ) { data.wireframeLinewidth = this.wireframeLinewidth; } - if ( this.wireframeLinecap !== 'round' ) { data.wireframeLinecap = this.wireframeLinecap; } - if ( this.wireframeLinejoin !== 'round' ) { data.wireframeLinejoin = this.wireframeLinejoin; } - - if ( this.morphTargets === true ) { data.morphTargets = true; } - if ( this.morphNormals === true ) { data.morphNormals = true; } - if ( this.skinning === true ) { data.skinning = true; } - - if ( this.visible === false ) { data.visible = false; } - - if ( this.toneMapped === false ) { data.toneMapped = false; } - - if ( JSON.stringify( this.userData ) !== '{}' ) { data.userData = this.userData; } - - // TODO: Copied from Object3D.toJSON - - function extractFromCache( cache ) { - - var values = []; - - for ( var key in cache ) { - - var data = cache[ key ]; - delete data.metadata; - values.push( data ); - - } - - return values; - - } - - if ( isRoot ) { - - var textures = extractFromCache( meta.textures ); - var images = extractFromCache( meta.images ); - - if ( textures.length > 0 ) { data.textures = textures; } - if ( images.length > 0 ) { data.images = images; } - - } - - return data; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.name = source.name; - - this.fog = source.fog; - - this.blending = source.blending; - this.side = source.side; - this.flatShading = source.flatShading; - this.vertexColors = source.vertexColors; - - this.opacity = source.opacity; - this.transparent = source.transparent; - - this.blendSrc = source.blendSrc; - this.blendDst = source.blendDst; - this.blendEquation = source.blendEquation; - this.blendSrcAlpha = source.blendSrcAlpha; - this.blendDstAlpha = source.blendDstAlpha; - this.blendEquationAlpha = source.blendEquationAlpha; - - this.depthFunc = source.depthFunc; - this.depthTest = source.depthTest; - this.depthWrite = source.depthWrite; - - this.stencilWriteMask = source.stencilWriteMask; - this.stencilFunc = source.stencilFunc; - this.stencilRef = source.stencilRef; - this.stencilFuncMask = source.stencilFuncMask; - this.stencilFail = source.stencilFail; - this.stencilZFail = source.stencilZFail; - this.stencilZPass = source.stencilZPass; - this.stencilWrite = source.stencilWrite; - - var srcPlanes = source.clippingPlanes, - dstPlanes = null; - - if ( srcPlanes !== null ) { - - var n = srcPlanes.length; - dstPlanes = new Array( n ); - - for ( var i = 0; i !== n; ++ i ) - { dstPlanes[ i ] = srcPlanes[ i ].clone(); } - - } - - this.clippingPlanes = dstPlanes; - this.clipIntersection = source.clipIntersection; - this.clipShadows = source.clipShadows; - - this.shadowSide = source.shadowSide; - - this.colorWrite = source.colorWrite; - - this.precision = source.precision; - - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; - - this.dithering = source.dithering; - - this.alphaTest = source.alphaTest; - this.premultipliedAlpha = source.premultipliedAlpha; - - this.visible = source.visible; - - this.toneMapped = source.toneMapped; - - this.userData = JSON.parse( JSON.stringify( source.userData ) ); - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - Object.defineProperty( Material.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) { this.version ++; } - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: - * } - */ - - function MeshBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshBasicMaterial'; - - this.color = new Color( 0xffffff ); // emissive - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - - this.setValues( parameters ); - - } - - MeshBasicMaterial.prototype = Object.create( Material.prototype ); - MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; - - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - - MeshBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var _vector$3 = new Vector3(); - - function BufferAttribute( array, itemSize, normalized ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.name = ''; - - this.array = array; - this.itemSize = itemSize; - this.count = array !== undefined ? array.length / itemSize : 0; - this.normalized = normalized === true; - - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - } - - Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) { this.version ++; } - - } - - } ); - - Object.assign( BufferAttribute.prototype, { - - isBufferAttribute: true, - - onUploadCallback: function () {}, - - setUsage: function ( value ) { - - this.usage = value; - - return this; - - }, - - copy: function ( source ) { - - this.name = source.name; - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; - - this.usage = source.usage; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.itemSize; - index2 *= attribute.itemSize; - - for ( var i = 0, l = this.itemSize; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - copyArray: function ( array ) { - - this.array.set( array ); - - return this; - - }, - - copyColorsArray: function ( colors ) { - - var array = this.array, offset = 0; - - for ( var i = 0, l = colors.length; i < l; i ++ ) { - - var color = colors[ i ]; - - if ( color === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); - color = new Color(); - - } - - array[ offset ++ ] = color.r; - array[ offset ++ ] = color.g; - array[ offset ++ ] = color.b; - - } - - return this; - - }, - - copyVector2sArray: function ( vectors ) { - - var array = this.array, offset = 0; - - for ( var i = 0, l = vectors.length; i < l; i ++ ) { - - var vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); - vector = new Vector2(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - - } - - return this; - - }, - - copyVector3sArray: function ( vectors ) { - - var array = this.array, offset = 0; - - for ( var i = 0, l = vectors.length; i < l; i ++ ) { - - var vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); - vector = new Vector3(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - - } - - return this; - - }, - - copyVector4sArray: function ( vectors ) { - - var array = this.array, offset = 0; - - for ( var i = 0, l = vectors.length; i < l; i ++ ) { - - var vector = vectors[ i ]; - - if ( vector === undefined ) { - - console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); - vector = new Vector4(); - - } - - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - array[ offset ++ ] = vector.w; - - } - - return this; - - }, - - applyMatrix3: function ( m ) { - - for ( var i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.applyMatrix3( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - applyMatrix4: function ( m ) { - - for ( var i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.applyMatrix4( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - applyNormalMatrix: function ( m ) { - - for ( var i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.applyNormalMatrix( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - transformDirection: function ( m ) { - - for ( var i = 0, l = this.count; i < l; i ++ ) { - - _vector$3.x = this.getX( i ); - _vector$3.y = this.getY( i ); - _vector$3.z = this.getZ( i ); - - _vector$3.transformDirection( m ); - - this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z ); - - } - - return this; - - }, - - set: function ( value, offset ) { - - if ( offset === undefined ) { offset = 0; } - - this.array.set( value, offset ); - - return this; - - }, - - getX: function ( index ) { - - return this.array[ index * this.itemSize ]; - - }, - - setX: function ( index, x ) { - - this.array[ index * this.itemSize ] = x; - - return this; - - }, - - getY: function ( index ) { - - return this.array[ index * this.itemSize + 1 ]; - - }, - - setY: function ( index, y ) { - - this.array[ index * this.itemSize + 1 ] = y; - - return this; - - }, - - getZ: function ( index ) { - - return this.array[ index * this.itemSize + 2 ]; - - }, - - setZ: function ( index, z ) { - - this.array[ index * this.itemSize + 2 ] = z; - - return this; - - }, - - getW: function ( index ) { - - return this.array[ index * this.itemSize + 3 ]; - - }, - - setW: function ( index, w ) { - - this.array[ index * this.itemSize + 3 ] = w; - - return this; - - }, - - setXY: function ( index, x, y ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - - return this; - - }, - - setXYZ: function ( index, x, y, z ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - - return this; - - }, - - setXYZW: function ( index, x, y, z, w ) { - - index *= this.itemSize; - - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - this.array[ index + 3 ] = w; - - return this; - - }, - - onUpload: function ( callback ) { - - this.onUploadCallback = callback; - - return this; - - }, - - clone: function () { - - return new this.constructor( this.array, this.itemSize ).copy( this ); - - }, - - toJSON: function () { - - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: Array.prototype.slice.call( this.array ), - normalized: this.normalized - }; - - } - - } ); - - // - - function Int8BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized ); - - } - - Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; - - - function Uint8BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized ); - - } - - Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; - - - function Uint8ClampedBufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized ); - - } - - Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; - - - function Int16BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized ); - - } - - Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; - - - function Uint16BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); - - } - - Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; - - - function Int32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized ); - - } - - Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; - - - function Uint32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized ); - - } - - Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; - - - function Float32BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); - - } - - Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; - - - function Float64BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized ); - - } - - Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function DirectGeometry() { - - this.vertices = []; - this.normals = []; - this.colors = []; - this.uvs = []; - this.uvs2 = []; - - this.groups = []; - - this.morphTargets = {}; - - this.skinWeights = []; - this.skinIndices = []; - - // this.lineDistances = []; - - this.boundingBox = null; - this.boundingSphere = null; - - // update flags - - this.verticesNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.uvsNeedUpdate = false; - this.groupsNeedUpdate = false; - - } - - Object.assign( DirectGeometry.prototype, { - - computeGroups: function ( geometry ) { - - var group; - var groups = []; - var materialIndex = undefined; - - var faces = geometry.faces; - - for ( var i = 0; i < faces.length; i ++ ) { - - var face = faces[ i ]; - - // materials - - if ( face.materialIndex !== materialIndex ) { - - materialIndex = face.materialIndex; - - if ( group !== undefined ) { - - group.count = ( i * 3 ) - group.start; - groups.push( group ); - - } - - group = { - start: i * 3, - materialIndex: materialIndex - }; - - } - - } - - if ( group !== undefined ) { - - group.count = ( i * 3 ) - group.start; - groups.push( group ); - - } - - this.groups = groups; - - }, - - fromGeometry: function ( geometry ) { - - var faces = geometry.faces; - var vertices = geometry.vertices; - var faceVertexUvs = geometry.faceVertexUvs; - - var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; - var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; - - // morphs - - var morphTargets = geometry.morphTargets; - var morphTargetsLength = morphTargets.length; - - var morphTargetsPosition; - - if ( morphTargetsLength > 0 ) { - - morphTargetsPosition = []; - - for ( var i = 0; i < morphTargetsLength; i ++ ) { - - morphTargetsPosition[ i ] = { - name: morphTargets[ i ].name, - data: [] - }; - - } - - this.morphTargets.position = morphTargetsPosition; - - } - - var morphNormals = geometry.morphNormals; - var morphNormalsLength = morphNormals.length; - - var morphTargetsNormal; - - if ( morphNormalsLength > 0 ) { - - morphTargetsNormal = []; - - for ( var i = 0; i < morphNormalsLength; i ++ ) { - - morphTargetsNormal[ i ] = { - name: morphNormals[ i ].name, - data: [] - }; - - } - - this.morphTargets.normal = morphTargetsNormal; - - } - - // skins - - var skinIndices = geometry.skinIndices; - var skinWeights = geometry.skinWeights; - - var hasSkinIndices = skinIndices.length === vertices.length; - var hasSkinWeights = skinWeights.length === vertices.length; - - // - - if ( vertices.length > 0 && faces.length === 0 ) { - - console.error( 'THREE.DirectGeometry: Faceless geometries are not supported.' ); - - } - - for ( var i = 0; i < faces.length; i ++ ) { - - var face = faces[ i ]; - - this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); - - var vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); - - } else { - - var normal = face.normal; - - this.normals.push( normal, normal, normal ); - - } - - var vertexColors = face.vertexColors; - - if ( vertexColors.length === 3 ) { - - this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); - - } else { - - var color = face.color; - - this.colors.push( color, color, color ); - - } - - if ( hasFaceVertexUv === true ) { - - var vertexUvs = faceVertexUvs[ 0 ][ i ]; - - if ( vertexUvs !== undefined ) { - - this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - - } else { - - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); - - this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); - - } - - } - - if ( hasFaceVertexUv2 === true ) { - - var vertexUvs = faceVertexUvs[ 1 ][ i ]; - - if ( vertexUvs !== undefined ) { - - this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - - } else { - - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); - - this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); - - } - - } - - // morphs - - for ( var j = 0; j < morphTargetsLength; j ++ ) { - - var morphTarget = morphTargets[ j ].vertices; - - morphTargetsPosition[ j ].data.push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); - - } - - for ( var j = 0; j < morphNormalsLength; j ++ ) { - - var morphNormal = morphNormals[ j ].vertexNormals[ i ]; - - morphTargetsNormal[ j ].data.push( morphNormal.a, morphNormal.b, morphNormal.c ); - - } - - // skins - - if ( hasSkinIndices ) { - - this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); - - } - - if ( hasSkinWeights ) { - - this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); - - } - - } - - this.computeGroups( geometry ); - - this.verticesNeedUpdate = geometry.verticesNeedUpdate; - this.normalsNeedUpdate = geometry.normalsNeedUpdate; - this.colorsNeedUpdate = geometry.colorsNeedUpdate; - this.uvsNeedUpdate = geometry.uvsNeedUpdate; - this.groupsNeedUpdate = geometry.groupsNeedUpdate; - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function arrayMax( array ) { - - if ( array.length === 0 ) { return - Infinity; } - - var max = array[ 0 ]; - - for ( var i = 1, l = array.length; i < l; ++ i ) { - - if ( array[ i ] > max ) { max = array[ i ]; } - - } - - return max; - - } - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - - var _bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id - - var _m1$2 = new Matrix4(); - var _obj = new Object3D(); - var _offset = new Vector3(); - var _box$2 = new Box3(); - var _boxMorphTargets = new Box3(); - var _vector$4 = new Vector3(); - - function BufferGeometry() { - - Object.defineProperty( this, 'id', { value: _bufferGeometryId += 2 } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'BufferGeometry'; - - this.index = null; - this.attributes = {}; - - this.morphAttributes = {}; - this.morphTargetsRelative = false; - - this.groups = []; - - this.boundingBox = null; - this.boundingSphere = null; - - this.drawRange = { start: 0, count: Infinity }; - - this.userData = {}; - - } - - BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: BufferGeometry, - - isBufferGeometry: true, - - getIndex: function () { - - return this.index; - - }, - - setIndex: function ( index ) { - - if ( Array.isArray( index ) ) { - - this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); - - } else { - - this.index = index; - - } - - }, - - getAttribute: function ( name ) { - - return this.attributes[ name ]; - - }, - - setAttribute: function ( name, attribute ) { - - this.attributes[ name ] = attribute; - - return this; - - }, - - deleteAttribute: function ( name ) { - - delete this.attributes[ name ]; - - return this; - - }, - - addGroup: function ( start, count, materialIndex ) { - - this.groups.push( { - - start: start, - count: count, - materialIndex: materialIndex !== undefined ? materialIndex : 0 - - } ); - - }, - - clearGroups: function () { - - this.groups = []; - - }, - - setDrawRange: function ( start, count ) { - - this.drawRange.start = start; - this.drawRange.count = count; - - }, - - applyMatrix4: function ( matrix ) { - - var position = this.attributes.position; - - if ( position !== undefined ) { - - position.applyMatrix4( matrix ); - - position.needsUpdate = true; - - } - - var normal = this.attributes.normal; - - if ( normal !== undefined ) { - - var normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - normal.applyNormalMatrix( normalMatrix ); - - normal.needsUpdate = true; - - } - - var tangent = this.attributes.tangent; - - if ( tangent !== undefined ) { - - tangent.transformDirection( matrix ); - - tangent.needsUpdate = true; - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - return this; - - }, - - rotateX: function ( angle ) { - - // rotate geometry around world x-axis - - _m1$2.makeRotationX( angle ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - rotateY: function ( angle ) { - - // rotate geometry around world y-axis - - _m1$2.makeRotationY( angle ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - rotateZ: function ( angle ) { - - // rotate geometry around world z-axis - - _m1$2.makeRotationZ( angle ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - translate: function ( x, y, z ) { - - // translate geometry - - _m1$2.makeTranslation( x, y, z ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - scale: function ( x, y, z ) { - - // scale geometry - - _m1$2.makeScale( x, y, z ); - - this.applyMatrix4( _m1$2 ); - - return this; - - }, - - lookAt: function ( vector ) { - - _obj.lookAt( vector ); - - _obj.updateMatrix(); - - this.applyMatrix4( _obj.matrix ); - - return this; - - }, - - center: function () { - - this.computeBoundingBox(); - - this.boundingBox.getCenter( _offset ).negate(); - - this.translate( _offset.x, _offset.y, _offset.z ); - - return this; - - }, - - setFromObject: function ( object ) { - - // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); - - var geometry = object.geometry; - - if ( object.isPoints || object.isLine ) { - - var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); - var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); - - this.setAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); - this.setAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); - - if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { - - var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); - - this.setAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); - - } - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - } else if ( object.isMesh ) { - - if ( geometry && geometry.isGeometry ) { - - this.fromGeometry( geometry ); - - } - - } - - return this; - - }, - - setFromPoints: function ( points ) { - - var position = []; - - for ( var i = 0, l = points.length; i < l; i ++ ) { - - var point = points[ i ]; - position.push( point.x, point.y, point.z || 0 ); - - } - - this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); - - return this; - - }, - - updateFromObject: function ( object ) { - - var geometry = object.geometry; - - if ( object.isMesh ) { - - var direct = geometry.__directGeometry; - - if ( geometry.elementsNeedUpdate === true ) { - - direct = undefined; - geometry.elementsNeedUpdate = false; - - } - - if ( direct === undefined ) { - - return this.fromGeometry( geometry ); - - } - - direct.verticesNeedUpdate = geometry.verticesNeedUpdate; - direct.normalsNeedUpdate = geometry.normalsNeedUpdate; - direct.colorsNeedUpdate = geometry.colorsNeedUpdate; - direct.uvsNeedUpdate = geometry.uvsNeedUpdate; - direct.groupsNeedUpdate = geometry.groupsNeedUpdate; - - geometry.verticesNeedUpdate = false; - geometry.normalsNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.uvsNeedUpdate = false; - geometry.groupsNeedUpdate = false; - - geometry = direct; - - } - - var attribute; - - if ( geometry.verticesNeedUpdate === true ) { - - attribute = this.attributes.position; - - if ( attribute !== undefined ) { - - attribute.copyVector3sArray( geometry.vertices ); - attribute.needsUpdate = true; - - } - - geometry.verticesNeedUpdate = false; - - } - - if ( geometry.normalsNeedUpdate === true ) { - - attribute = this.attributes.normal; - - if ( attribute !== undefined ) { - - attribute.copyVector3sArray( geometry.normals ); - attribute.needsUpdate = true; - - } - - geometry.normalsNeedUpdate = false; - - } - - if ( geometry.colorsNeedUpdate === true ) { - - attribute = this.attributes.color; - - if ( attribute !== undefined ) { - - attribute.copyColorsArray( geometry.colors ); - attribute.needsUpdate = true; - - } - - geometry.colorsNeedUpdate = false; - - } - - if ( geometry.uvsNeedUpdate ) { - - attribute = this.attributes.uv; - - if ( attribute !== undefined ) { - - attribute.copyVector2sArray( geometry.uvs ); - attribute.needsUpdate = true; - - } - - geometry.uvsNeedUpdate = false; - - } - - if ( geometry.lineDistancesNeedUpdate ) { - - attribute = this.attributes.lineDistance; - - if ( attribute !== undefined ) { - - attribute.copyArray( geometry.lineDistances ); - attribute.needsUpdate = true; - - } - - geometry.lineDistancesNeedUpdate = false; - - } - - if ( geometry.groupsNeedUpdate ) { - - geometry.computeGroups( object.geometry ); - this.groups = geometry.groups; - - geometry.groupsNeedUpdate = false; - - } - - return this; - - }, - - fromGeometry: function ( geometry ) { - - geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); - - return this.fromDirectGeometry( geometry.__directGeometry ); - - }, - - fromDirectGeometry: function ( geometry ) { - - var positions = new Float32Array( geometry.vertices.length * 3 ); - this.setAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); - - if ( geometry.normals.length > 0 ) { - - var normals = new Float32Array( geometry.normals.length * 3 ); - this.setAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); - - } - - if ( geometry.colors.length > 0 ) { - - var colors = new Float32Array( geometry.colors.length * 3 ); - this.setAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); - - } - - if ( geometry.uvs.length > 0 ) { - - var uvs = new Float32Array( geometry.uvs.length * 2 ); - this.setAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); - - } - - if ( geometry.uvs2.length > 0 ) { - - var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); - this.setAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); - - } - - // groups - - this.groups = geometry.groups; - - // morphs - - for ( var name in geometry.morphTargets ) { - - var array = []; - var morphTargets = geometry.morphTargets[ name ]; - - for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { - - var morphTarget = morphTargets[ i ]; - - var attribute = new Float32BufferAttribute( morphTarget.data.length * 3, 3 ); - attribute.name = morphTarget.name; - - array.push( attribute.copyVector3sArray( morphTarget.data ) ); - - } - - this.morphAttributes[ name ] = array; - - } - - // skinning - - if ( geometry.skinIndices.length > 0 ) { - - var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); - this.setAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); - - } - - if ( geometry.skinWeights.length > 0 ) { - - var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); - this.setAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); - - } - - // - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - return this; - - }, - - computeBoundingBox: function () { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - var position = this.attributes.position; - var morphAttributesPosition = this.morphAttributes.position; - - if ( position !== undefined ) { - - this.boundingBox.setFromBufferAttribute( position ); - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( var i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - var morphAttribute = morphAttributesPosition[ i ]; - _box$2.setFromBufferAttribute( morphAttribute ); - - if ( this.morphTargetsRelative ) { - - _vector$4.addVectors( this.boundingBox.min, _box$2.min ); - this.boundingBox.expandByPoint( _vector$4 ); - - _vector$4.addVectors( this.boundingBox.max, _box$2.max ); - this.boundingBox.expandByPoint( _vector$4 ); - - } else { - - this.boundingBox.expandByPoint( _box$2.min ); - this.boundingBox.expandByPoint( _box$2.max ); - - } - - } - - } - - } else { - - this.boundingBox.makeEmpty(); - - } - - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); - - } - - }, - - computeBoundingSphere: function () { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - var position = this.attributes.position; - var morphAttributesPosition = this.morphAttributes.position; - - if ( position ) { - - // first, find the center of the bounding sphere - - var center = this.boundingSphere.center; - - _box$2.setFromBufferAttribute( position ); - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( var i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - var morphAttribute = morphAttributesPosition[ i ]; - _boxMorphTargets.setFromBufferAttribute( morphAttribute ); - - if ( this.morphTargetsRelative ) { - - _vector$4.addVectors( _box$2.min, _boxMorphTargets.min ); - _box$2.expandByPoint( _vector$4 ); - - _vector$4.addVectors( _box$2.max, _boxMorphTargets.max ); - _box$2.expandByPoint( _vector$4 ); - - } else { - - _box$2.expandByPoint( _boxMorphTargets.min ); - _box$2.expandByPoint( _boxMorphTargets.max ); - - } - - } - - } - - _box$2.getCenter( center ); - - // second, try to find a boundingSphere with a radius smaller than the - // boundingSphere of the boundingBox: sqrt(3) smaller in the best case - - var maxRadiusSq = 0; - - for ( var i = 0, il = position.count; i < il; i ++ ) { - - _vector$4.fromBufferAttribute( position, i ); - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); - - } - - // process morph attributes if present - - if ( morphAttributesPosition ) { - - for ( var i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - - var morphAttribute = morphAttributesPosition[ i ]; - var morphTargetsRelative = this.morphTargetsRelative; - - for ( var j = 0, jl = morphAttribute.count; j < jl; j ++ ) { - - _vector$4.fromBufferAttribute( morphAttribute, j ); - - if ( morphTargetsRelative ) { - - _offset.fromBufferAttribute( position, j ); - _vector$4.add( _offset ); - - } - - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); - - } - - } - - } - - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - - if ( isNaN( this.boundingSphere.radius ) ) { - - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); - - } - - } - - }, - - computeFaceNormals: function () { - - // backwards compatibility - - }, - - computeVertexNormals: function () { - - var index = this.index; - var attributes = this.attributes; - - if ( attributes.position ) { - - var positions = attributes.position.array; - - if ( attributes.normal === undefined ) { - - this.setAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) ); - - } else { - - // reset existing normals to zero - - var array = attributes.normal.array; - - for ( var i = 0, il = array.length; i < il; i ++ ) { - - array[ i ] = 0; - - } - - } - - var normals = attributes.normal.array; - - var vA, vB, vC; - var pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); - var cb = new Vector3(), ab = new Vector3(); - - // indexed elements - - if ( index ) { - - var indices = index.array; - - for ( var i = 0, il = index.count; i < il; i += 3 ) { - - vA = indices[ i + 0 ] * 3; - vB = indices[ i + 1 ] * 3; - vC = indices[ i + 2 ] * 3; - - pA.fromArray( positions, vA ); - pB.fromArray( positions, vB ); - pC.fromArray( positions, vC ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normals[ vA ] += cb.x; - normals[ vA + 1 ] += cb.y; - normals[ vA + 2 ] += cb.z; - - normals[ vB ] += cb.x; - normals[ vB + 1 ] += cb.y; - normals[ vB + 2 ] += cb.z; - - normals[ vC ] += cb.x; - normals[ vC + 1 ] += cb.y; - normals[ vC + 2 ] += cb.z; - - } - - } else { - - // non-indexed elements (unconnected triangle soup) - - for ( var i = 0, il = positions.length; i < il; i += 9 ) { - - pA.fromArray( positions, i ); - pB.fromArray( positions, i + 3 ); - pC.fromArray( positions, i + 6 ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normals[ i ] = cb.x; - normals[ i + 1 ] = cb.y; - normals[ i + 2 ] = cb.z; - - normals[ i + 3 ] = cb.x; - normals[ i + 4 ] = cb.y; - normals[ i + 5 ] = cb.z; - - normals[ i + 6 ] = cb.x; - normals[ i + 7 ] = cb.y; - normals[ i + 8 ] = cb.z; - - } - - } - - this.normalizeNormals(); - - attributes.normal.needsUpdate = true; - - } - - }, - - merge: function ( geometry, offset ) { - - if ( ! ( geometry && geometry.isBufferGeometry ) ) { - - console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); - return; - - } - - if ( offset === undefined ) { - - offset = 0; - - console.warn( - 'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. ' - + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.' - ); - - } - - var attributes = this.attributes; - - for ( var key in attributes ) { - - if ( geometry.attributes[ key ] === undefined ) { continue; } - - var attribute1 = attributes[ key ]; - var attributeArray1 = attribute1.array; - - var attribute2 = geometry.attributes[ key ]; - var attributeArray2 = attribute2.array; - - var attributeOffset = attribute2.itemSize * offset; - var length = Math.min( attributeArray2.length, attributeArray1.length - attributeOffset ); - - for ( var i = 0, j = attributeOffset; i < length; i ++, j ++ ) { - - attributeArray1[ j ] = attributeArray2[ i ]; - - } - - } - - return this; - - }, - - normalizeNormals: function () { - - var normals = this.attributes.normal; - - for ( var i = 0, il = normals.count; i < il; i ++ ) { - - _vector$4.x = normals.getX( i ); - _vector$4.y = normals.getY( i ); - _vector$4.z = normals.getZ( i ); - - _vector$4.normalize(); - - normals.setXYZ( i, _vector$4.x, _vector$4.y, _vector$4.z ); - - } - - }, - - toNonIndexed: function () { - - function convertBufferAttribute( attribute, indices ) { - - var array = attribute.array; - var itemSize = attribute.itemSize; - var normalized = attribute.normalized; - - var array2 = new array.constructor( indices.length * itemSize ); - - var index = 0, index2 = 0; - - for ( var i = 0, l = indices.length; i < l; i ++ ) { - - index = indices[ i ] * itemSize; - - for ( var j = 0; j < itemSize; j ++ ) { - - array2[ index2 ++ ] = array[ index ++ ]; - - } - - } - - return new BufferAttribute( array2, itemSize, normalized ); - - } - - // - - if ( this.index === null ) { - - console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); - return this; - - } - - var geometry2 = new BufferGeometry(); - - var indices = this.index.array; - var attributes = this.attributes; - - // attributes - - for ( var name in attributes ) { - - var attribute = attributes[ name ]; - - var newAttribute = convertBufferAttribute( attribute, indices ); - - geometry2.setAttribute( name, newAttribute ); - - } - - // morph attributes - - var morphAttributes = this.morphAttributes; - - for ( name in morphAttributes ) { - - var morphArray = []; - var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - - for ( var i = 0, il = morphAttribute.length; i < il; i ++ ) { - - var attribute = morphAttribute[ i ]; - - var newAttribute = convertBufferAttribute( attribute, indices ); - - morphArray.push( newAttribute ); - - } - - geometry2.morphAttributes[ name ] = morphArray; - - } - - geometry2.morphTargetsRelative = this.morphTargetsRelative; - - // groups - - var groups = this.groups; - - for ( var i = 0, l = groups.length; i < l; i ++ ) { - - var group = groups[ i ]; - geometry2.addGroup( group.start, group.count, group.materialIndex ); - - } - - return geometry2; - - }, - - toJSON: function () { - - var data = { - metadata: { - version: 4.5, - type: 'BufferGeometry', - generator: 'BufferGeometry.toJSON' - } - }; - - // standard BufferGeometry serialization - - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) { data.name = this.name; } - if ( Object.keys( this.userData ).length > 0 ) { data.userData = this.userData; } - - if ( this.parameters !== undefined ) { - - var parameters = this.parameters; - - for ( var key in parameters ) { - - if ( parameters[ key ] !== undefined ) { data[ key ] = parameters[ key ]; } - - } - - return data; - - } - - data.data = { attributes: {} }; - - var index = this.index; - - if ( index !== null ) { - - data.data.index = { - type: index.array.constructor.name, - array: Array.prototype.slice.call( index.array ) - }; - - } - - var attributes = this.attributes; - - for ( var key in attributes ) { - - var attribute = attributes[ key ]; - - var attributeData = attribute.toJSON(); - - if ( attribute.name !== '' ) { attributeData.name = attribute.name; } - - data.data.attributes[ key ] = attributeData; - - } - - var morphAttributes = {}; - var hasMorphAttributes = false; - - for ( var key in this.morphAttributes ) { - - var attributeArray = this.morphAttributes[ key ]; - - var array = []; - - for ( var i = 0, il = attributeArray.length; i < il; i ++ ) { - - var attribute = attributeArray[ i ]; - - var attributeData = attribute.toJSON(); - - if ( attribute.name !== '' ) { attributeData.name = attribute.name; } - - array.push( attributeData ); - - } - - if ( array.length > 0 ) { - - morphAttributes[ key ] = array; - - hasMorphAttributes = true; - - } - - } - - if ( hasMorphAttributes ) { - - data.data.morphAttributes = morphAttributes; - data.data.morphTargetsRelative = this.morphTargetsRelative; - - } - - var groups = this.groups; - - if ( groups.length > 0 ) { - - data.data.groups = JSON.parse( JSON.stringify( groups ) ); - - } - - var boundingSphere = this.boundingSphere; - - if ( boundingSphere !== null ) { - - data.data.boundingSphere = { - center: boundingSphere.center.toArray(), - radius: boundingSphere.radius - }; - - } - - return data; - - }, - - clone: function () { - - /* - // Handle primitives - - var parameters = this.parameters; - - if ( parameters !== undefined ) { - - var values = []; - - for ( var key in parameters ) { - - values.push( parameters[ key ] ); - - } - - var geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - - return new this.constructor().copy( this ); - */ - - return new BufferGeometry().copy( this ); - - }, - - copy: function ( source ) { - - var name, i, l; - - // reset - - this.index = null; - this.attributes = {}; - this.morphAttributes = {}; - this.groups = []; - this.boundingBox = null; - this.boundingSphere = null; - - // name - - this.name = source.name; - - // index - - var index = source.index; - - if ( index !== null ) { - - this.setIndex( index.clone() ); - - } - - // attributes - - var attributes = source.attributes; - - for ( name in attributes ) { - - var attribute = attributes[ name ]; - this.setAttribute( name, attribute.clone() ); - - } - - // morph attributes - - var morphAttributes = source.morphAttributes; - - for ( name in morphAttributes ) { - - var array = []; - var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - - for ( i = 0, l = morphAttribute.length; i < l; i ++ ) { - - array.push( morphAttribute[ i ].clone() ); - - } - - this.morphAttributes[ name ] = array; - - } - - this.morphTargetsRelative = source.morphTargetsRelative; - - // groups - - var groups = source.groups; - - for ( i = 0, l = groups.length; i < l; i ++ ) { - - var group = groups[ i ]; - this.addGroup( group.start, group.count, group.materialIndex ); - - } - - // bounding box - - var boundingBox = source.boundingBox; - - if ( boundingBox !== null ) { - - this.boundingBox = boundingBox.clone(); - - } - - // bounding sphere - - var boundingSphere = source.boundingSphere; - - if ( boundingSphere !== null ) { - - this.boundingSphere = boundingSphere.clone(); - - } - - // draw range - - this.drawRange.start = source.drawRange.start; - this.drawRange.count = source.drawRange.count; - - // user data - - this.userData = source.userData; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author jonobr1 / http://jonobr1.com/ - */ - - var _inverseMatrix = new Matrix4(); - var _ray = new Ray(); - var _sphere = new Sphere(); - - var _vA = new Vector3(); - var _vB = new Vector3(); - var _vC = new Vector3(); - - var _tempA = new Vector3(); - var _tempB = new Vector3(); - var _tempC = new Vector3(); - - var _morphA = new Vector3(); - var _morphB = new Vector3(); - var _morphC = new Vector3(); - - var _uvA = new Vector2(); - var _uvB = new Vector2(); - var _uvC = new Vector2(); - - var _intersectionPoint = new Vector3(); - var _intersectionPointWorld = new Vector3(); - - function Mesh( geometry, material ) { - - Object3D.call( this ); - - this.type = 'Mesh'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new MeshBasicMaterial(); - - this.updateMorphTargets(); - - } - - Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Mesh, - - isMesh: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - if ( source.morphTargetInfluences !== undefined ) { - - this.morphTargetInfluences = source.morphTargetInfluences.slice(); - - } - - if ( source.morphTargetDictionary !== undefined ) { - - this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); - - } - - return this; - - }, - - updateMorphTargets: function () { - - var geometry = this.geometry; - var m, ml, name; - - if ( geometry.isBufferGeometry ) { - - var morphAttributes = geometry.morphAttributes; - var keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - var morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - var morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - }, - - raycast: function ( raycaster, intersects ) { - - var geometry = this.geometry; - var material = this.material; - var matrixWorld = this.matrixWorld; - - if ( material === undefined ) { return; } - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) { geometry.computeBoundingSphere(); } - - _sphere.copy( geometry.boundingSphere ); - _sphere.applyMatrix4( matrixWorld ); - - if ( raycaster.ray.intersectsSphere( _sphere ) === false ) { return; } - - // - - _inverseMatrix.getInverse( matrixWorld ); - _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); - - // Check boundingBox before continuing - - if ( geometry.boundingBox !== null ) { - - if ( _ray.intersectsBox( geometry.boundingBox ) === false ) { return; } - - } - - var intersection; - - if ( geometry.isBufferGeometry ) { - - var a, b, c; - var index = geometry.index; - var position = geometry.attributes.position; - var morphPosition = geometry.morphAttributes.position; - var morphTargetsRelative = geometry.morphTargetsRelative; - var uv = geometry.attributes.uv; - var uv2 = geometry.attributes.uv2; - var groups = geometry.groups; - var drawRange = geometry.drawRange; - var i, j, il, jl; - var group, groupMaterial; - var start, end; - - if ( index !== null ) { - - // indexed buffer geometry - - if ( Array.isArray( material ) ) { - - for ( i = 0, il = groups.length; i < il; i ++ ) { - - group = groups[ i ]; - groupMaterial = material[ group.materialIndex ]; - - start = Math.max( group.start, drawRange.start ); - end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); - - for ( j = start, jl = end; j < jl; j += 3 ) { - - a = index.getX( j ); - b = index.getX( j + 1 ); - c = index.getX( j + 2 ); - - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); - - } - - } - - } - - } else { - - start = Math.max( 0, drawRange.start ); - end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - - for ( i = start, il = end; i < il; i += 3 ) { - - a = index.getX( i ); - b = index.getX( i + 1 ); - c = index.getX( i + 2 ); - - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics - intersects.push( intersection ); - - } - - } - - } - - } else if ( position !== undefined ) { - - // non-indexed buffer geometry - - if ( Array.isArray( material ) ) { - - for ( i = 0, il = groups.length; i < il; i ++ ) { - - group = groups[ i ]; - groupMaterial = material[ group.materialIndex ]; - - start = Math.max( group.start, drawRange.start ); - end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); - - for ( j = start, jl = end; j < jl; j += 3 ) { - - a = j; - b = j + 1; - c = j + 2; - - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); - - } - - } - - } - - } else { - - start = Math.max( 0, drawRange.start ); - end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); - - for ( i = start, il = end; i < il; i += 3 ) { - - a = i; - b = i + 1; - c = i + 2; - - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ); - - if ( intersection ) { - - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics - intersects.push( intersection ); - - } - - } - - } - - } - - } else if ( geometry.isGeometry ) { - - var fvA, fvB, fvC; - var isMultiMaterial = Array.isArray( material ); - - var vertices = geometry.vertices; - var faces = geometry.faces; - var uvs; - - var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; - if ( faceVertexUvs.length > 0 ) { uvs = faceVertexUvs; } - - for ( var f = 0, fl = faces.length; f < fl; f ++ ) { - - var face = faces[ f ]; - var faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material; - - if ( faceMaterial === undefined ) { continue; } - - fvA = vertices[ face.a ]; - fvB = vertices[ face.b ]; - fvC = vertices[ face.c ]; - - intersection = checkIntersection( this, faceMaterial, raycaster, _ray, fvA, fvB, fvC, _intersectionPoint ); - - if ( intersection ) { - - if ( uvs && uvs[ f ] ) { - - var uvs_f = uvs[ f ]; - _uvA.copy( uvs_f[ 0 ] ); - _uvB.copy( uvs_f[ 1 ] ); - _uvC.copy( uvs_f[ 2 ] ); - - intersection.uv = Triangle.getUV( _intersectionPoint, fvA, fvB, fvC, _uvA, _uvB, _uvC, new Vector2() ); - - } - - intersection.face = face; - intersection.faceIndex = f; - intersects.push( intersection ); - - } - - } - - } - - }, - - clone: function () { - - return new this.constructor( this.geometry, this.material ).copy( this ); - - } - - } ); - - function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { - - var intersect; - - if ( material.side === BackSide ) { - - intersect = ray.intersectTriangle( pC, pB, pA, true, point ); - - } else { - - intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); - - } - - if ( intersect === null ) { return null; } - - _intersectionPointWorld.copy( point ); - _intersectionPointWorld.applyMatrix4( object.matrixWorld ); - - var distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); - - if ( distance < raycaster.near || distance > raycaster.far ) { return null; } - - return { - distance: distance, - point: _intersectionPointWorld.clone(), - object: object - }; - - } - - function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ) { - - _vA.fromBufferAttribute( position, a ); - _vB.fromBufferAttribute( position, b ); - _vC.fromBufferAttribute( position, c ); - - var morphInfluences = object.morphTargetInfluences; - - if ( material.morphTargets && morphPosition && morphInfluences ) { - - _morphA.set( 0, 0, 0 ); - _morphB.set( 0, 0, 0 ); - _morphC.set( 0, 0, 0 ); - - for ( var i = 0, il = morphPosition.length; i < il; i ++ ) { - - var influence = morphInfluences[ i ]; - var morphAttribute = morphPosition[ i ]; - - if ( influence === 0 ) { continue; } - - _tempA.fromBufferAttribute( morphAttribute, a ); - _tempB.fromBufferAttribute( morphAttribute, b ); - _tempC.fromBufferAttribute( morphAttribute, c ); - - if ( morphTargetsRelative ) { - - _morphA.addScaledVector( _tempA, influence ); - _morphB.addScaledVector( _tempB, influence ); - _morphC.addScaledVector( _tempC, influence ); - - } else { - - _morphA.addScaledVector( _tempA.sub( _vA ), influence ); - _morphB.addScaledVector( _tempB.sub( _vB ), influence ); - _morphC.addScaledVector( _tempC.sub( _vC ), influence ); - - } - - } - - _vA.add( _morphA ); - _vB.add( _morphB ); - _vC.add( _morphC ); - - } - - if ( object.isSkinnedMesh ) { - - object.boneTransform( a, _vA ); - object.boneTransform( b, _vB ); - object.boneTransform( c, _vC ); - - } - - var intersection = checkIntersection( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint ); - - if ( intersection ) { - - if ( uv ) { - - _uvA.fromBufferAttribute( uv, a ); - _uvB.fromBufferAttribute( uv, b ); - _uvC.fromBufferAttribute( uv, c ); - - intersection.uv = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); - - } - - if ( uv2 ) { - - _uvA.fromBufferAttribute( uv2, a ); - _uvB.fromBufferAttribute( uv2, b ); - _uvC.fromBufferAttribute( uv2, c ); - - intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); - - } - - var face = new Face3( a, b, c ); - Triangle.getNormal( _vA, _vB, _vC, face.normal ); - - intersection.face = face; - - } - - return intersection; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - * @author kile / http://kile.stravaganza.org/ - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author bhouston / http://clara.io - */ - - var _geometryId = 0; // Geometry uses even numbers as Id - var _m1$3 = new Matrix4(); - var _obj$1 = new Object3D(); - var _offset$1 = new Vector3(); - - function Geometry() { - - Object.defineProperty( this, 'id', { value: _geometryId += 2 } ); - - this.uuid = MathUtils.generateUUID(); - - this.name = ''; - this.type = 'Geometry'; - - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [[]]; - - this.morphTargets = []; - this.morphNormals = []; - - this.skinWeights = []; - this.skinIndices = []; - - this.lineDistances = []; - - this.boundingBox = null; - this.boundingSphere = null; - - // update flags - - this.elementsNeedUpdate = false; - this.verticesNeedUpdate = false; - this.uvsNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.lineDistancesNeedUpdate = false; - this.groupsNeedUpdate = false; - - } - - Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: Geometry, - - isGeometry: true, - - applyMatrix4: function ( matrix ) { - - var normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { - - var vertex = this.vertices[ i ]; - vertex.applyMatrix4( matrix ); - - } - - for ( var i = 0, il = this.faces.length; i < il; i ++ ) { - - var face = this.faces[ i ]; - face.normal.applyMatrix3( normalMatrix ).normalize(); - - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - - face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); - - } - - } - - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); - - } - - if ( this.boundingSphere !== null ) { - - this.computeBoundingSphere(); - - } - - this.verticesNeedUpdate = true; - this.normalsNeedUpdate = true; - - return this; - - }, - - rotateX: function ( angle ) { - - // rotate geometry around world x-axis - - _m1$3.makeRotationX( angle ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - rotateY: function ( angle ) { - - // rotate geometry around world y-axis - - _m1$3.makeRotationY( angle ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - rotateZ: function ( angle ) { - - // rotate geometry around world z-axis - - _m1$3.makeRotationZ( angle ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - translate: function ( x, y, z ) { - - // translate geometry - - _m1$3.makeTranslation( x, y, z ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - scale: function ( x, y, z ) { - - // scale geometry - - _m1$3.makeScale( x, y, z ); - - this.applyMatrix4( _m1$3 ); - - return this; - - }, - - lookAt: function ( vector ) { - - _obj$1.lookAt( vector ); - - _obj$1.updateMatrix(); - - this.applyMatrix4( _obj$1.matrix ); - - return this; - - }, - - fromBufferGeometry: function ( geometry ) { - - var scope = this; - - var indices = geometry.index !== null ? geometry.index.array : undefined; - var attributes = geometry.attributes; - - if ( attributes.position === undefined ) { - - console.error( 'THREE.Geometry.fromBufferGeometry(): Position attribute required for conversion.' ); - return this; - - } - - var positions = attributes.position.array; - var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; - var colors = attributes.color !== undefined ? attributes.color.array : undefined; - var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; - var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; - - if ( uvs2 !== undefined ) { this.faceVertexUvs[ 1 ] = []; } - - for ( var i = 0; i < positions.length; i += 3 ) { - - scope.vertices.push( new Vector3().fromArray( positions, i ) ); - - if ( colors !== undefined ) { - - scope.colors.push( new Color().fromArray( colors, i ) ); - - } - - } - - function addFace( a, b, c, materialIndex ) { - - var vertexColors = ( colors === undefined ) ? [] : [ - scope.colors[ a ].clone(), - scope.colors[ b ].clone(), - scope.colors[ c ].clone() ]; - - var vertexNormals = ( normals === undefined ) ? [] : [ - new Vector3().fromArray( normals, a * 3 ), - new Vector3().fromArray( normals, b * 3 ), - new Vector3().fromArray( normals, c * 3 ) - ]; - - var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); - - scope.faces.push( face ); - - if ( uvs !== undefined ) { - - scope.faceVertexUvs[ 0 ].push( [ - new Vector2().fromArray( uvs, a * 2 ), - new Vector2().fromArray( uvs, b * 2 ), - new Vector2().fromArray( uvs, c * 2 ) - ] ); - - } - - if ( uvs2 !== undefined ) { - - scope.faceVertexUvs[ 1 ].push( [ - new Vector2().fromArray( uvs2, a * 2 ), - new Vector2().fromArray( uvs2, b * 2 ), - new Vector2().fromArray( uvs2, c * 2 ) - ] ); - - } - - } - - var groups = geometry.groups; - - if ( groups.length > 0 ) { - - for ( var i = 0; i < groups.length; i ++ ) { - - var group = groups[ i ]; - - var start = group.start; - var count = group.count; - - for ( var j = start, jl = start + count; j < jl; j += 3 ) { - - if ( indices !== undefined ) { - - addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); - - } else { - - addFace( j, j + 1, j + 2, group.materialIndex ); - - } - - } - - } - - } else { - - if ( indices !== undefined ) { - - for ( var i = 0; i < indices.length; i += 3 ) { - - addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); - - } - - } else { - - for ( var i = 0; i < positions.length / 3; i += 3 ) { - - addFace( i, i + 1, i + 2 ); - - } - - } - - } - - this.computeFaceNormals(); - - if ( geometry.boundingBox !== null ) { - - this.boundingBox = geometry.boundingBox.clone(); - - } - - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); - - } - - return this; - - }, - - center: function () { - - this.computeBoundingBox(); - - this.boundingBox.getCenter( _offset$1 ).negate(); - - this.translate( _offset$1.x, _offset$1.y, _offset$1.z ); - - return this; - - }, - - normalize: function () { - - this.computeBoundingSphere(); - - var center = this.boundingSphere.center; - var radius = this.boundingSphere.radius; - - var s = radius === 0 ? 1 : 1.0 / radius; - - var matrix = new Matrix4(); - matrix.set( - s, 0, 0, - s * center.x, - 0, s, 0, - s * center.y, - 0, 0, s, - s * center.z, - 0, 0, 0, 1 - ); - - this.applyMatrix4( matrix ); - - return this; - - }, - - computeFaceNormals: function () { - - var cb = new Vector3(), ab = new Vector3(); - - for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { - - var face = this.faces[ f ]; - - var vA = this.vertices[ face.a ]; - var vB = this.vertices[ face.b ]; - var vC = this.vertices[ face.c ]; - - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); - - cb.normalize(); - - face.normal.copy( cb ); - - } - - }, - - computeVertexNormals: function ( areaWeighted ) { - - if ( areaWeighted === undefined ) { areaWeighted = true; } - - var v, vl, f, fl, face, vertices; - - vertices = new Array( this.vertices.length ); - - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - - vertices[ v ] = new Vector3(); - - } - - if ( areaWeighted ) { - - // vertex normals weighted by triangle areas - // http://www.iquilezles.org/www/articles/normals/normals.htm - - var vA, vB, vC; - var cb = new Vector3(), ab = new Vector3(); - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - vA = this.vertices[ face.a ]; - vB = this.vertices[ face.b ]; - vC = this.vertices[ face.c ]; - - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); - - vertices[ face.a ].add( cb ); - vertices[ face.b ].add( cb ); - vertices[ face.c ].add( cb ); - - } - - } else { - - this.computeFaceNormals(); - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - vertices[ face.a ].add( face.normal ); - vertices[ face.b ].add( face.normal ); - vertices[ face.c ].add( face.normal ); - - } - - } - - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - - vertices[ v ].normalize(); - - } - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - var vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - vertexNormals[ 0 ].copy( vertices[ face.a ] ); - vertexNormals[ 1 ].copy( vertices[ face.b ] ); - vertexNormals[ 2 ].copy( vertices[ face.c ] ); - - } else { - - vertexNormals[ 0 ] = vertices[ face.a ].clone(); - vertexNormals[ 1 ] = vertices[ face.b ].clone(); - vertexNormals[ 2 ] = vertices[ face.c ].clone(); - - } - - } - - if ( this.faces.length > 0 ) { - - this.normalsNeedUpdate = true; - - } - - }, - - computeFlatVertexNormals: function () { - - var f, fl, face; - - this.computeFaceNormals(); - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - var vertexNormals = face.vertexNormals; - - if ( vertexNormals.length === 3 ) { - - vertexNormals[ 0 ].copy( face.normal ); - vertexNormals[ 1 ].copy( face.normal ); - vertexNormals[ 2 ].copy( face.normal ); - - } else { - - vertexNormals[ 0 ] = face.normal.clone(); - vertexNormals[ 1 ] = face.normal.clone(); - vertexNormals[ 2 ] = face.normal.clone(); - - } - - } - - if ( this.faces.length > 0 ) { - - this.normalsNeedUpdate = true; - - } - - }, - - computeMorphNormals: function () { - - var i, il, f, fl, face; - - // save original normals - // - create temp variables on first access - // otherwise just copy (for faster repeated calls) - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - if ( ! face.__originalFaceNormal ) { - - face.__originalFaceNormal = face.normal.clone(); - - } else { - - face.__originalFaceNormal.copy( face.normal ); - - } - - if ( ! face.__originalVertexNormals ) { face.__originalVertexNormals = []; } - - for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { - - if ( ! face.__originalVertexNormals[ i ] ) { - - face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); - - } else { - - face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); - - } - - } - - } - - // use temp geometry to compute face and vertex normals for each morph - - var tmpGeo = new Geometry(); - tmpGeo.faces = this.faces; - - for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { - - // create on first access - - if ( ! this.morphNormals[ i ] ) { - - this.morphNormals[ i ] = {}; - this.morphNormals[ i ].faceNormals = []; - this.morphNormals[ i ].vertexNormals = []; - - var dstNormalsFace = this.morphNormals[ i ].faceNormals; - var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; - - var faceNormal, vertexNormals; - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - faceNormal = new Vector3(); - vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; - - dstNormalsFace.push( faceNormal ); - dstNormalsVertex.push( vertexNormals ); - - } - - } - - var morphNormals = this.morphNormals[ i ]; - - // set vertices to morph target - - tmpGeo.vertices = this.morphTargets[ i ].vertices; - - // compute morph normals - - tmpGeo.computeFaceNormals(); - tmpGeo.computeVertexNormals(); - - // store morph normals - - var faceNormal, vertexNormals; - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - faceNormal = morphNormals.faceNormals[ f ]; - vertexNormals = morphNormals.vertexNormals[ f ]; - - faceNormal.copy( face.normal ); - - vertexNormals.a.copy( face.vertexNormals[ 0 ] ); - vertexNormals.b.copy( face.vertexNormals[ 1 ] ); - vertexNormals.c.copy( face.vertexNormals[ 2 ] ); - - } - - } - - // restore original normals - - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - face.normal = face.__originalFaceNormal; - face.vertexNormals = face.__originalVertexNormals; - - } - - }, - - computeBoundingBox: function () { - - if ( this.boundingBox === null ) { - - this.boundingBox = new Box3(); - - } - - this.boundingBox.setFromPoints( this.vertices ); - - }, - - computeBoundingSphere: function () { - - if ( this.boundingSphere === null ) { - - this.boundingSphere = new Sphere(); - - } - - this.boundingSphere.setFromPoints( this.vertices ); - - }, - - merge: function ( geometry, matrix, materialIndexOffset ) { - - if ( ! ( geometry && geometry.isGeometry ) ) { - - console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); - return; - - } - - var normalMatrix, - vertexOffset = this.vertices.length, - vertices1 = this.vertices, - vertices2 = geometry.vertices, - faces1 = this.faces, - faces2 = geometry.faces, - colors1 = this.colors, - colors2 = geometry.colors; - - if ( materialIndexOffset === undefined ) { materialIndexOffset = 0; } - - if ( matrix !== undefined ) { - - normalMatrix = new Matrix3().getNormalMatrix( matrix ); - - } - - // vertices - - for ( var i = 0, il = vertices2.length; i < il; i ++ ) { - - var vertex = vertices2[ i ]; - - var vertexCopy = vertex.clone(); - - if ( matrix !== undefined ) { vertexCopy.applyMatrix4( matrix ); } - - vertices1.push( vertexCopy ); - - } - - // colors - - for ( var i = 0, il = colors2.length; i < il; i ++ ) { - - colors1.push( colors2[ i ].clone() ); - - } - - // faces - - for ( i = 0, il = faces2.length; i < il; i ++ ) { - - var face = faces2[ i ], faceCopy, normal, color, - faceVertexNormals = face.vertexNormals, - faceVertexColors = face.vertexColors; - - faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); - faceCopy.normal.copy( face.normal ); - - if ( normalMatrix !== undefined ) { - - faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); - - } - - for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { - - normal = faceVertexNormals[ j ].clone(); - - if ( normalMatrix !== undefined ) { - - normal.applyMatrix3( normalMatrix ).normalize(); - - } - - faceCopy.vertexNormals.push( normal ); - - } - - faceCopy.color.copy( face.color ); - - for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { - - color = faceVertexColors[ j ]; - faceCopy.vertexColors.push( color.clone() ); - - } - - faceCopy.materialIndex = face.materialIndex + materialIndexOffset; - - faces1.push( faceCopy ); - - } - - // uvs - - for ( var i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { - - var faceVertexUvs2 = geometry.faceVertexUvs[ i ]; - - if ( this.faceVertexUvs[ i ] === undefined ) { this.faceVertexUvs[ i ] = []; } - - for ( var j = 0, jl = faceVertexUvs2.length; j < jl; j ++ ) { - - var uvs2 = faceVertexUvs2[ j ], uvsCopy = []; - - for ( var k = 0, kl = uvs2.length; k < kl; k ++ ) { - - uvsCopy.push( uvs2[ k ].clone() ); - - } - - this.faceVertexUvs[ i ].push( uvsCopy ); - - } - - } - - }, - - mergeMesh: function ( mesh ) { - - if ( ! ( mesh && mesh.isMesh ) ) { - - console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); - return; - - } - - if ( mesh.matrixAutoUpdate ) { mesh.updateMatrix(); } - - this.merge( mesh.geometry, mesh.matrix ); - - }, - - /* - * Checks for duplicate vertices with hashmap. - * Duplicated vertices are removed - * and faces' vertices are updated. - */ - - mergeVertices: function () { - - var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) - var unique = [], changes = []; - - var v, key; - var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 - var precision = Math.pow( 10, precisionPoints ); - var i, il, face; - var indices, j, jl; - - for ( i = 0, il = this.vertices.length; i < il; i ++ ) { - - v = this.vertices[ i ]; - key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); - - if ( verticesMap[ key ] === undefined ) { - - verticesMap[ key ] = i; - unique.push( this.vertices[ i ] ); - changes[ i ] = unique.length - 1; - - } else { - - //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); - changes[ i ] = changes[ verticesMap[ key ] ]; - - } - - } - - - // if faces are completely degenerate after merging vertices, we - // have to remove them from the geometry. - var faceIndicesToRemove = []; - - for ( i = 0, il = this.faces.length; i < il; i ++ ) { - - face = this.faces[ i ]; - - face.a = changes[ face.a ]; - face.b = changes[ face.b ]; - face.c = changes[ face.c ]; - - indices = [ face.a, face.b, face.c ]; - - // if any duplicate vertices are found in a Face3 - // we have to remove the face as nothing can be saved - for ( var n = 0; n < 3; n ++ ) { - - if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { - - faceIndicesToRemove.push( i ); - break; - - } - - } - - } - - for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { - - var idx = faceIndicesToRemove[ i ]; - - this.faces.splice( idx, 1 ); - - for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { - - this.faceVertexUvs[ j ].splice( idx, 1 ); - - } - - } - - // Use unique set of vertices - - var diff = this.vertices.length - unique.length; - this.vertices = unique; - return diff; - - }, - - setFromPoints: function ( points ) { - - this.vertices = []; - - for ( var i = 0, l = points.length; i < l; i ++ ) { - - var point = points[ i ]; - this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); - - } - - return this; - - }, - - sortFacesByMaterialIndex: function () { - - var faces = this.faces; - var length = faces.length; - - // tag faces - - for ( var i = 0; i < length; i ++ ) { - - faces[ i ]._id = i; - - } - - // sort faces - - function materialIndexSort( a, b ) { - - return a.materialIndex - b.materialIndex; - - } - - faces.sort( materialIndexSort ); - - // sort uvs - - var uvs1 = this.faceVertexUvs[ 0 ]; - var uvs2 = this.faceVertexUvs[ 1 ]; - - var newUvs1, newUvs2; - - if ( uvs1 && uvs1.length === length ) { newUvs1 = []; } - if ( uvs2 && uvs2.length === length ) { newUvs2 = []; } - - for ( var i = 0; i < length; i ++ ) { - - var id = faces[ i ]._id; - - if ( newUvs1 ) { newUvs1.push( uvs1[ id ] ); } - if ( newUvs2 ) { newUvs2.push( uvs2[ id ] ); } - - } - - if ( newUvs1 ) { this.faceVertexUvs[ 0 ] = newUvs1; } - if ( newUvs2 ) { this.faceVertexUvs[ 1 ] = newUvs2; } - - }, - - toJSON: function () { - - var data = { - metadata: { - version: 4.5, - type: 'Geometry', - generator: 'Geometry.toJSON' - } - }; - - // standard Geometry serialization - - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) { data.name = this.name; } - - if ( this.parameters !== undefined ) { - - var parameters = this.parameters; - - for ( var key in parameters ) { - - if ( parameters[ key ] !== undefined ) { data[ key ] = parameters[ key ]; } - - } - - return data; - - } - - var vertices = []; - - for ( var i = 0; i < this.vertices.length; i ++ ) { - - var vertex = this.vertices[ i ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - var faces = []; - var normals = []; - var normalsHash = {}; - var colors = []; - var colorsHash = {}; - var uvs = []; - var uvsHash = {}; - - for ( var i = 0; i < this.faces.length; i ++ ) { - - var face = this.faces[ i ]; - - var hasMaterial = true; - var hasFaceUv = false; // deprecated - var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; - var hasFaceNormal = face.normal.length() > 0; - var hasFaceVertexNormal = face.vertexNormals.length > 0; - var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; - var hasFaceVertexColor = face.vertexColors.length > 0; - - var faceType = 0; - - faceType = setBit( faceType, 0, 0 ); // isQuad - faceType = setBit( faceType, 1, hasMaterial ); - faceType = setBit( faceType, 2, hasFaceUv ); - faceType = setBit( faceType, 3, hasFaceVertexUv ); - faceType = setBit( faceType, 4, hasFaceNormal ); - faceType = setBit( faceType, 5, hasFaceVertexNormal ); - faceType = setBit( faceType, 6, hasFaceColor ); - faceType = setBit( faceType, 7, hasFaceVertexColor ); - - faces.push( faceType ); - faces.push( face.a, face.b, face.c ); - faces.push( face.materialIndex ); - - if ( hasFaceVertexUv ) { - - var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; - - faces.push( - getUvIndex( faceVertexUvs[ 0 ] ), - getUvIndex( faceVertexUvs[ 1 ] ), - getUvIndex( faceVertexUvs[ 2 ] ) - ); - - } - - if ( hasFaceNormal ) { - - faces.push( getNormalIndex( face.normal ) ); - - } - - if ( hasFaceVertexNormal ) { - - var vertexNormals = face.vertexNormals; - - faces.push( - getNormalIndex( vertexNormals[ 0 ] ), - getNormalIndex( vertexNormals[ 1 ] ), - getNormalIndex( vertexNormals[ 2 ] ) - ); - - } - - if ( hasFaceColor ) { - - faces.push( getColorIndex( face.color ) ); - - } - - if ( hasFaceVertexColor ) { - - var vertexColors = face.vertexColors; - - faces.push( - getColorIndex( vertexColors[ 0 ] ), - getColorIndex( vertexColors[ 1 ] ), - getColorIndex( vertexColors[ 2 ] ) - ); - - } - - } - - function setBit( value, position, enabled ) { - - return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); - - } - - function getNormalIndex( normal ) { - - var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); - - if ( normalsHash[ hash ] !== undefined ) { - - return normalsHash[ hash ]; - - } - - normalsHash[ hash ] = normals.length / 3; - normals.push( normal.x, normal.y, normal.z ); - - return normalsHash[ hash ]; - - } - - function getColorIndex( color ) { - - var hash = color.r.toString() + color.g.toString() + color.b.toString(); - - if ( colorsHash[ hash ] !== undefined ) { - - return colorsHash[ hash ]; - - } - - colorsHash[ hash ] = colors.length; - colors.push( color.getHex() ); - - return colorsHash[ hash ]; - - } - - function getUvIndex( uv ) { - - var hash = uv.x.toString() + uv.y.toString(); - - if ( uvsHash[ hash ] !== undefined ) { - - return uvsHash[ hash ]; - - } - - uvsHash[ hash ] = uvs.length / 2; - uvs.push( uv.x, uv.y ); - - return uvsHash[ hash ]; - - } - - data.data = {}; - - data.data.vertices = vertices; - data.data.normals = normals; - if ( colors.length > 0 ) { data.data.colors = colors; } - if ( uvs.length > 0 ) { data.data.uvs = [ uvs ]; } // temporal backward compatibility - data.data.faces = faces; - - return data; - - }, - - clone: function () { - - /* - // Handle primitives - - var parameters = this.parameters; - - if ( parameters !== undefined ) { - - var values = []; - - for ( var key in parameters ) { - - values.push( parameters[ key ] ); - - } - - var geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - - return new this.constructor().copy( this ); - */ - - return new Geometry().copy( this ); - - }, - - copy: function ( source ) { - - var i, il, j, jl, k, kl; - - // reset - - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [[]]; - this.morphTargets = []; - this.morphNormals = []; - this.skinWeights = []; - this.skinIndices = []; - this.lineDistances = []; - this.boundingBox = null; - this.boundingSphere = null; - - // name - - this.name = source.name; - - // vertices - - var vertices = source.vertices; - - for ( i = 0, il = vertices.length; i < il; i ++ ) { - - this.vertices.push( vertices[ i ].clone() ); - - } - - // colors - - var colors = source.colors; - - for ( i = 0, il = colors.length; i < il; i ++ ) { - - this.colors.push( colors[ i ].clone() ); - - } - - // faces - - var faces = source.faces; - - for ( i = 0, il = faces.length; i < il; i ++ ) { - - this.faces.push( faces[ i ].clone() ); - - } - - // face vertex uvs - - for ( i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { - - var faceVertexUvs = source.faceVertexUvs[ i ]; - - if ( this.faceVertexUvs[ i ] === undefined ) { - - this.faceVertexUvs[ i ] = []; - - } - - for ( j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { - - var uvs = faceVertexUvs[ j ], uvsCopy = []; - - for ( k = 0, kl = uvs.length; k < kl; k ++ ) { - - var uv = uvs[ k ]; - - uvsCopy.push( uv.clone() ); - - } - - this.faceVertexUvs[ i ].push( uvsCopy ); - - } - - } - - // morph targets - - var morphTargets = source.morphTargets; - - for ( i = 0, il = morphTargets.length; i < il; i ++ ) { - - var morphTarget = {}; - morphTarget.name = morphTargets[ i ].name; - - // vertices - - if ( morphTargets[ i ].vertices !== undefined ) { - - morphTarget.vertices = []; - - for ( j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) { - - morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() ); - - } - - } - - // normals - - if ( morphTargets[ i ].normals !== undefined ) { - - morphTarget.normals = []; - - for ( j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) { - - morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() ); - - } - - } - - this.morphTargets.push( morphTarget ); - - } - - // morph normals - - var morphNormals = source.morphNormals; - - for ( i = 0, il = morphNormals.length; i < il; i ++ ) { - - var morphNormal = {}; - - // vertex normals - - if ( morphNormals[ i ].vertexNormals !== undefined ) { - - morphNormal.vertexNormals = []; - - for ( j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) { - - var srcVertexNormal = morphNormals[ i ].vertexNormals[ j ]; - var destVertexNormal = {}; - - destVertexNormal.a = srcVertexNormal.a.clone(); - destVertexNormal.b = srcVertexNormal.b.clone(); - destVertexNormal.c = srcVertexNormal.c.clone(); - - morphNormal.vertexNormals.push( destVertexNormal ); - - } - - } - - // face normals - - if ( morphNormals[ i ].faceNormals !== undefined ) { - - morphNormal.faceNormals = []; - - for ( j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) { - - morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() ); - - } - - } - - this.morphNormals.push( morphNormal ); - - } - - // skin weights - - var skinWeights = source.skinWeights; - - for ( i = 0, il = skinWeights.length; i < il; i ++ ) { - - this.skinWeights.push( skinWeights[ i ].clone() ); - - } - - // skin indices - - var skinIndices = source.skinIndices; - - for ( i = 0, il = skinIndices.length; i < il; i ++ ) { - - this.skinIndices.push( skinIndices[ i ].clone() ); - - } - - // line distances - - var lineDistances = source.lineDistances; - - for ( i = 0, il = lineDistances.length; i < il; i ++ ) { - - this.lineDistances.push( lineDistances[ i ] ); - - } - - // bounding box - - var boundingBox = source.boundingBox; - - if ( boundingBox !== null ) { - - this.boundingBox = boundingBox.clone(); - - } - - // bounding sphere - - var boundingSphere = source.boundingSphere; - - if ( boundingSphere !== null ) { - - this.boundingSphere = boundingSphere.clone(); - - } - - // update flags - - this.elementsNeedUpdate = source.elementsNeedUpdate; - this.verticesNeedUpdate = source.verticesNeedUpdate; - this.uvsNeedUpdate = source.uvsNeedUpdate; - this.normalsNeedUpdate = source.normalsNeedUpdate; - this.colorsNeedUpdate = source.colorsNeedUpdate; - this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate; - this.groupsNeedUpdate = source.groupsNeedUpdate; - - return this; - - }, - - dispose: function () { - - this.dispatchEvent( { type: 'dispose' } ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ - - // BoxGeometry - - var BoxGeometry = /*@__PURE__*/(function (Geometry) { - function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { - - Geometry.call(this); - - this.type = 'BoxGeometry'; - - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; - - this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); - this.mergeVertices(); - - } - - if ( Geometry ) BoxGeometry.__proto__ = Geometry; - BoxGeometry.prototype = Object.create( Geometry && Geometry.prototype ); - BoxGeometry.prototype.constructor = BoxGeometry; - - return BoxGeometry; - }(Geometry)); - - // BoxBufferGeometry - - var BoxBufferGeometry = /*@__PURE__*/(function (BufferGeometry) { - function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { - - BufferGeometry.call(this); - - this.type = 'BoxBufferGeometry'; - - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; - - var scope = this; - - width = width || 1; - height = height || 1; - depth = depth || 1; - - // segments - - widthSegments = Math.floor( widthSegments ) || 1; - heightSegments = Math.floor( heightSegments ) || 1; - depthSegments = Math.floor( depthSegments ) || 1; - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - // helper variables - - var numberOfVertices = 0; - var groupStart = 0; - - // build each side of the box geometry - - buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px - buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx - buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py - buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny - buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz - buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { - - var segmentWidth = width / gridX; - var segmentHeight = height / gridY; - - var widthHalf = width / 2; - var heightHalf = height / 2; - var depthHalf = depth / 2; - - var gridX1 = gridX + 1; - var gridY1 = gridY + 1; - - var vertexCounter = 0; - var groupCount = 0; - - var vector = new Vector3(); - - // generate vertices, normals and uvs - - for ( var iy = 0; iy < gridY1; iy ++ ) { - - var y = iy * segmentHeight - heightHalf; - - for ( var ix = 0; ix < gridX1; ix ++ ) { - - var x = ix * segmentWidth - widthHalf; - - // set values to correct vector component - - vector[ u ] = x * udir; - vector[ v ] = y * vdir; - vector[ w ] = depthHalf; - - // now apply vector to vertex buffer - - vertices.push( vector.x, vector.y, vector.z ); - - // set values to correct vector component - - vector[ u ] = 0; - vector[ v ] = 0; - vector[ w ] = depth > 0 ? 1 : - 1; - - // now apply vector to normal buffer - - normals.push( vector.x, vector.y, vector.z ); - - // uvs - - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); - - // counters - - vertexCounter += 1; - - } - - } - - // indices - - // 1. you need three indices to draw a single face - // 2. a single segment consists of two faces - // 3. so we need to generate six (2*3) indices per segment - - for ( var iy$1 = 0; iy$1 < gridY; iy$1 ++ ) { - - for ( var ix$1 = 0; ix$1 < gridX; ix$1 ++ ) { - - var a = numberOfVertices + ix$1 + gridX1 * iy$1; - var b = numberOfVertices + ix$1 + gridX1 * ( iy$1 + 1 ); - var c = numberOfVertices + ( ix$1 + 1 ) + gridX1 * ( iy$1 + 1 ); - var d = numberOfVertices + ( ix$1 + 1 ) + gridX1 * iy$1; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - // increase counter - - groupCount += 6; - - } - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, materialIndex ); - - // calculate new start value for groups - - groupStart += groupCount; - - // update total number of vertices - - numberOfVertices += vertexCounter; - - } - - } - - if ( BufferGeometry ) BoxBufferGeometry.__proto__ = BufferGeometry; - BoxBufferGeometry.prototype = Object.create( BufferGeometry && BufferGeometry.prototype ); - BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; - - return BoxBufferGeometry; - }(BufferGeometry)); - - /** - * Uniform Utilities - */ - - function cloneUniforms( src ) { - - var dst = {}; - - for ( var u in src ) { - - dst[ u ] = {}; - - for ( var p in src[ u ] ) { - - var property = src[ u ][ p ]; - - if ( property && ( property.isColor || - property.isMatrix3 || property.isMatrix4 || - property.isVector2 || property.isVector3 || property.isVector4 || - property.isTexture ) ) { - - dst[ u ][ p ] = property.clone(); - - } else if ( Array.isArray( property ) ) { - - dst[ u ][ p ] = property.slice(); - - } else { - - dst[ u ][ p ] = property; - - } - - } - - } - - return dst; - - } - - function mergeUniforms( uniforms ) { - - var merged = {}; - - for ( var u = 0; u < uniforms.length; u ++ ) { - - var tmp = cloneUniforms( uniforms[ u ] ); - - for ( var p in tmp ) { - - merged[ p ] = tmp[ p ]; - - } - - } - - return merged; - - } - - // Legacy - - var UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; - - var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; - - var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; - - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function ShaderMaterial( parameters ) { - - Material.call( this ); - - this.type = 'ShaderMaterial'; - - this.defines = {}; - this.uniforms = {}; - - this.vertexShader = default_vertex; - this.fragmentShader = default_fragment; - - this.linewidth = 1; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; // set to use scene fog - this.lights = false; // set to use scene lights - this.clipping = false; // set to use user-defined clipping planes - - this.skinning = false; // set to use skinning attribute streams - this.morphTargets = false; // set to use morph targets - this.morphNormals = false; // set to use morph normals - - this.extensions = { - derivatives: false, // set to use derivatives - fragDepth: false, // set to use fragment depth values - drawBuffers: false, // set to use draw buffers - shaderTextureLOD: false // set to use shader texture LOD - }; - - // When rendered geometry doesn't include these attributes but the material does, - // use these default values in WebGL. This avoids errors when buffer data is missing. - this.defaultAttributeValues = { - 'color': [ 1, 1, 1 ], - 'uv': [ 0, 0 ], - 'uv2': [ 0, 0 ] - }; - - this.index0AttributeName = undefined; - this.uniformsNeedUpdate = false; - - if ( parameters !== undefined ) { - - if ( parameters.attributes !== undefined ) { - - console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); - - } - - this.setValues( parameters ); - - } - - } - - ShaderMaterial.prototype = Object.create( Material.prototype ); - ShaderMaterial.prototype.constructor = ShaderMaterial; - - ShaderMaterial.prototype.isShaderMaterial = true; - - ShaderMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.fragmentShader = source.fragmentShader; - this.vertexShader = source.vertexShader; - - this.uniforms = cloneUniforms( source.uniforms ); - - this.defines = Object.assign( {}, source.defines ); - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.lights = source.lights; - this.clipping = source.clipping; - - this.skinning = source.skinning; - - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - this.extensions = Object.assign( {}, source.extensions ); - - return this; - - }; - - ShaderMaterial.prototype.toJSON = function ( meta ) { - - var data = Material.prototype.toJSON.call( this, meta ); - - data.uniforms = {}; - - for ( var name in this.uniforms ) { - - var uniform = this.uniforms[ name ]; - var value = uniform.value; - - if ( value && value.isTexture ) { - - data.uniforms[ name ] = { - type: 't', - value: value.toJSON( meta ).uuid - }; - - } else if ( value && value.isColor ) { - - data.uniforms[ name ] = { - type: 'c', - value: value.getHex() - }; - - } else if ( value && value.isVector2 ) { - - data.uniforms[ name ] = { - type: 'v2', - value: value.toArray() - }; - - } else if ( value && value.isVector3 ) { - - data.uniforms[ name ] = { - type: 'v3', - value: value.toArray() - }; - - } else if ( value && value.isVector4 ) { - - data.uniforms[ name ] = { - type: 'v4', - value: value.toArray() - }; - - } else if ( value && value.isMatrix3 ) { - - data.uniforms[ name ] = { - type: 'm3', - value: value.toArray() - }; - - } else if ( value && value.isMatrix4 ) { - - data.uniforms[ name ] = { - type: 'm4', - value: value.toArray() - }; - - } else { - - data.uniforms[ name ] = { - value: value - }; - - // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far - - } - - } - - if ( Object.keys( this.defines ).length > 0 ) { data.defines = this.defines; } - - data.vertexShader = this.vertexShader; - data.fragmentShader = this.fragmentShader; - - var extensions = {}; - - for ( var key in this.extensions ) { - - if ( this.extensions[ key ] === true ) { extensions[ key ] = true; } - - } - - if ( Object.keys( extensions ).length > 0 ) { data.extensions = extensions; } - - return data; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - * @author WestLangley / http://github.com/WestLangley - */ - - function Camera() { - - Object3D.call( this ); - - this.type = 'Camera'; - - this.matrixWorldInverse = new Matrix4(); - - this.projectionMatrix = new Matrix4(); - this.projectionMatrixInverse = new Matrix4(); - - } - - Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Camera, - - isCamera: true, - - copy: function ( source, recursive ) { - - Object3D.prototype.copy.call( this, source, recursive ); - - this.matrixWorldInverse.copy( source.matrixWorldInverse ); - - this.projectionMatrix.copy( source.projectionMatrix ); - this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); - - return this; - - }, - - getWorldDirection: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Camera: .getWorldDirection() target is now required' ); - target = new Vector3(); - - } - - this.updateMatrixWorld( true ); - - var e = this.matrixWorld.elements; - - return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); - - }, - - updateMatrixWorld: function ( force ) { - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - this.matrixWorldInverse.getInverse( this.matrixWorld ); - - }, - - updateWorldMatrix: function ( updateParents, updateChildren ) { - - Object3D.prototype.updateWorldMatrix.call( this, updateParents, updateChildren ); - - this.matrixWorldInverse.getInverse( this.matrixWorld ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author greggman / http://games.greggman.com/ - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author tschw - */ - - function PerspectiveCamera( fov, aspect, near, far ) { - - Camera.call( this ); - - this.type = 'PerspectiveCamera'; - - this.fov = fov !== undefined ? fov : 50; - this.zoom = 1; - - this.near = near !== undefined ? near : 0.1; - this.far = far !== undefined ? far : 2000; - this.focus = 10; - - this.aspect = aspect !== undefined ? aspect : 1; - this.view = null; - - this.filmGauge = 35; // width of the film (default in millimeters) - this.filmOffset = 0; // horizontal film offset (same unit as gauge) - - this.updateProjectionMatrix(); - - } - - PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { - - constructor: PerspectiveCamera, - - isPerspectiveCamera: true, - - copy: function ( source, recursive ) { - - Camera.prototype.copy.call( this, source, recursive ); - - this.fov = source.fov; - this.zoom = source.zoom; - - this.near = source.near; - this.far = source.far; - this.focus = source.focus; - - this.aspect = source.aspect; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - this.filmGauge = source.filmGauge; - this.filmOffset = source.filmOffset; - - return this; - - }, - - /** - * Sets the FOV by focal length in respect to the current .filmGauge. - * - * The default film gauge is 35, so that the focal length can be specified for - * a 35mm (full frame) camera. - * - * Values for focal length and film gauge must have the same unit. - */ - setFocalLength: function ( focalLength ) { - - // see http://www.bobatkins.com/photography/technical/field_of_view.html - var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; - - this.fov = MathUtils.RAD2DEG * 2 * Math.atan( vExtentSlope ); - this.updateProjectionMatrix(); - - }, - - /** - * Calculates the focal length from the current .fov and .filmGauge. - */ - getFocalLength: function () { - - var vExtentSlope = Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ); - - return 0.5 * this.getFilmHeight() / vExtentSlope; - - }, - - getEffectiveFOV: function () { - - return MathUtils.RAD2DEG * 2 * Math.atan( - Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom ); - - }, - - getFilmWidth: function () { - - // film not completely covered in portrait format (aspect < 1) - return this.filmGauge * Math.min( this.aspect, 1 ); - - }, - - getFilmHeight: function () { - - // film not completely covered in landscape format (aspect > 1) - return this.filmGauge / Math.max( this.aspect, 1 ); - - }, - - /** - * Sets an offset in a larger frustum. This is useful for multi-window or - * multi-monitor/multi-machine setups. - * - * For example, if you have 3x2 monitors and each monitor is 1920x1080 and - * the monitors are in grid like this - * - * +---+---+---+ - * | A | B | C | - * +---+---+---+ - * | D | E | F | - * +---+---+---+ - * - * then for each monitor you would call it like this - * - * var w = 1920; - * var h = 1080; - * var fullWidth = w * 3; - * var fullHeight = h * 2; - * - * --A-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); - * --B-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); - * --C-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); - * --D-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); - * --E-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); - * --F-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); - * - * Note there is no reason monitors have to be the same size or in a grid. - */ - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { - - this.aspect = fullWidth / fullHeight; - - if ( this.view === null ) { - - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; - - } - - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; - - this.updateProjectionMatrix(); - - }, - - clearViewOffset: function () { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - }, - - updateProjectionMatrix: function () { - - var near = this.near, - top = near * Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom, - height = 2 * top, - width = this.aspect * height, - left = - 0.5 * width, - view = this.view; - - if ( this.view !== null && this.view.enabled ) { - - var fullWidth = view.fullWidth, - fullHeight = view.fullHeight; - - left += view.offsetX * width / fullWidth; - top -= view.offsetY * height / fullHeight; - width *= view.width / fullWidth; - height *= view.height / fullHeight; - - } - - var skew = this.filmOffset; - if ( skew !== 0 ) { left += near * skew / this.getFilmWidth(); } - - this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); - - this.projectionMatrixInverse.getInverse( this.projectionMatrix ); - - }, - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.fov = this.fov; - data.object.zoom = this.zoom; - - data.object.near = this.near; - data.object.far = this.far; - data.object.focus = this.focus; - - data.object.aspect = this.aspect; - - if ( this.view !== null ) { data.object.view = Object.assign( {}, this.view ); } - - data.object.filmGauge = this.filmGauge; - data.object.filmOffset = this.filmOffset; - - return data; - - } - - } ); - - /** - * Camera for rendering cube maps - * - renders scene into axis-aligned cube - * - * @author alteredq / http://alteredqualia.com/ - */ - - var fov = 90, aspect = 1; - - function CubeCamera( near, far, renderTarget ) { - - Object3D.call( this ); - - this.type = 'CubeCamera'; - - if ( renderTarget.isWebGLCubeRenderTarget !== true ) { - - console.error( 'THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.' ); - return; - - } - - this.renderTarget = renderTarget; - - var cameraPX = new PerspectiveCamera( fov, aspect, near, far ); - cameraPX.layers = this.layers; - cameraPX.up.set( 0, - 1, 0 ); - cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); - this.add( cameraPX ); - - var cameraNX = new PerspectiveCamera( fov, aspect, near, far ); - cameraNX.layers = this.layers; - cameraNX.up.set( 0, - 1, 0 ); - cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); - this.add( cameraNX ); - - var cameraPY = new PerspectiveCamera( fov, aspect, near, far ); - cameraPY.layers = this.layers; - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); - - var cameraNY = new PerspectiveCamera( fov, aspect, near, far ); - cameraNY.layers = this.layers; - cameraNY.up.set( 0, 0, - 1 ); - cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); - this.add( cameraNY ); - - var cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.layers = this.layers; - cameraPZ.up.set( 0, - 1, 0 ); - cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); - this.add( cameraPZ ); - - var cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.layers = this.layers; - cameraNZ.up.set( 0, - 1, 0 ); - cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); - this.add( cameraNZ ); - - this.update = function ( renderer, scene ) { - - if ( this.parent === null ) { this.updateMatrixWorld(); } - - var currentXrEnabled = renderer.xr.enabled; - var currentRenderTarget = renderer.getRenderTarget(); - - renderer.xr.enabled = false; - - var generateMipmaps = renderTarget.texture.generateMipmaps; - - renderTarget.texture.generateMipmaps = false; - - renderer.setRenderTarget( renderTarget, 0 ); - renderer.render( scene, cameraPX ); - - renderer.setRenderTarget( renderTarget, 1 ); - renderer.render( scene, cameraNX ); - - renderer.setRenderTarget( renderTarget, 2 ); - renderer.render( scene, cameraPY ); - - renderer.setRenderTarget( renderTarget, 3 ); - renderer.render( scene, cameraNY ); - - renderer.setRenderTarget( renderTarget, 4 ); - renderer.render( scene, cameraPZ ); - - renderTarget.texture.generateMipmaps = generateMipmaps; - - renderer.setRenderTarget( renderTarget, 5 ); - renderer.render( scene, cameraNZ ); - - renderer.setRenderTarget( currentRenderTarget ); - - renderer.xr.enabled = currentXrEnabled; - - }; - - this.clear = function ( renderer, color, depth, stencil ) { - - var currentRenderTarget = renderer.getRenderTarget(); - - for ( var i = 0; i < 6; i ++ ) { - - renderer.setRenderTarget( renderTarget, i ); - - renderer.clear( color, depth, stencil ); - - } - - renderer.setRenderTarget( currentRenderTarget ); - - }; - - } - - CubeCamera.prototype = Object.create( Object3D.prototype ); - CubeCamera.prototype.constructor = CubeCamera; - - /** - * @author alteredq / http://alteredqualia.com - * @author WestLangley / http://github.com/WestLangley - */ - - function WebGLCubeRenderTarget( size, options, dummy ) { - - if ( Number.isInteger( options ) ) { - - console.warn( 'THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )' ); - - options = dummy; - - } - - WebGLRenderTarget.call( this, size, size, options ); - - } - - WebGLCubeRenderTarget.prototype = Object.create( WebGLRenderTarget.prototype ); - WebGLCubeRenderTarget.prototype.constructor = WebGLCubeRenderTarget; - - WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; - - WebGLCubeRenderTarget.prototype.fromEquirectangularTexture = function ( renderer, texture ) { - - this.texture.type = texture.type; - this.texture.format = texture.format; - this.texture.encoding = texture.encoding; - - var scene = new Scene(); - - var shader = { - - uniforms: { - tEquirect: { value: null }, - }, - - vertexShader: [ - - "varying vec3 vWorldDirection;", - - "vec3 transformDirection( in vec3 dir, in mat4 matrix ) {", - - " return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );", - - "}", - - "void main() {", - - " vWorldDirection = transformDirection( position, modelMatrix );", - - " #include ", - " #include ", - - "}" - - ].join( '\n' ), - - fragmentShader: [ - - "uniform sampler2D tEquirect;", - - "varying vec3 vWorldDirection;", - - "#include ", - - "void main() {", - - " vec3 direction = normalize( vWorldDirection );", - - " vec2 sampleUV = equirectUv( direction );", - - " gl_FragColor = texture2D( tEquirect, sampleUV );", - - "}" - - ].join( '\n' ), - }; - - var material = new ShaderMaterial( { - - type: 'CubemapFromEquirect', - - uniforms: cloneUniforms( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - side: BackSide, - blending: NoBlending - - } ); - - material.uniforms.tEquirect.value = texture; - - var mesh = new Mesh( new BoxBufferGeometry( 5, 5, 5 ), material ); - - scene.add( mesh ); - - var camera = new CubeCamera( 1, 10, this ); - camera.update( renderer, scene ); - - mesh.geometry.dispose(); - mesh.material.dispose(); - - return this; - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { data: data || null, width: width || 1, height: height || 1 }; - - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; - - this.needsUpdate = true; - - } - - DataTexture.prototype = Object.create( Texture.prototype ); - DataTexture.prototype.constructor = DataTexture; - - DataTexture.prototype.isDataTexture = true; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author bhouston / http://clara.io - */ - - var _sphere$1 = new Sphere(); - var _vector$5 = new Vector3(); - - function Frustum( p0, p1, p2, p3, p4, p5 ) { - - this.planes = [ - - ( p0 !== undefined ) ? p0 : new Plane(), - ( p1 !== undefined ) ? p1 : new Plane(), - ( p2 !== undefined ) ? p2 : new Plane(), - ( p3 !== undefined ) ? p3 : new Plane(), - ( p4 !== undefined ) ? p4 : new Plane(), - ( p5 !== undefined ) ? p5 : new Plane() - - ]; - - } - - Object.assign( Frustum.prototype, { - - set: function ( p0, p1, p2, p3, p4, p5 ) { - - var planes = this.planes; - - planes[ 0 ].copy( p0 ); - planes[ 1 ].copy( p1 ); - planes[ 2 ].copy( p2 ); - planes[ 3 ].copy( p3 ); - planes[ 4 ].copy( p4 ); - planes[ 5 ].copy( p5 ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( frustum ) { - - var planes = this.planes; - - for ( var i = 0; i < 6; i ++ ) { - - planes[ i ].copy( frustum.planes[ i ] ); - - } - - return this; - - }, - - setFromProjectionMatrix: function ( m ) { - - var planes = this.planes; - var me = m.elements; - var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; - var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; - var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; - var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; - - planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); - planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); - planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); - planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); - planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); - planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); - - return this; - - }, - - intersectsObject: function ( object ) { - - var geometry = object.geometry; - - if ( geometry.boundingSphere === null ) { geometry.computeBoundingSphere(); } - - _sphere$1.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); - - return this.intersectsSphere( _sphere$1 ); - - }, - - intersectsSprite: function ( sprite ) { - - _sphere$1.center.set( 0, 0, 0 ); - _sphere$1.radius = 0.7071067811865476; - _sphere$1.applyMatrix4( sprite.matrixWorld ); - - return this.intersectsSphere( _sphere$1 ); - - }, - - intersectsSphere: function ( sphere ) { - - var planes = this.planes; - var center = sphere.center; - var negRadius = - sphere.radius; - - for ( var i = 0; i < 6; i ++ ) { - - var distance = planes[ i ].distanceToPoint( center ); - - if ( distance < negRadius ) { - - return false; - - } - - } - - return true; - - }, - - intersectsBox: function ( box ) { - - var planes = this.planes; - - for ( var i = 0; i < 6; i ++ ) { - - var plane = planes[ i ]; - - // corner at max distance - - _vector$5.x = plane.normal.x > 0 ? box.max.x : box.min.x; - _vector$5.y = plane.normal.y > 0 ? box.max.y : box.min.y; - _vector$5.z = plane.normal.z > 0 ? box.max.z : box.min.z; - - if ( plane.distanceToPoint( _vector$5 ) < 0 ) { - - return false; - - } - - } - - return true; - - }, - - containsPoint: function ( point ) { - - var planes = this.planes; - - for ( var i = 0; i < 6; i ++ ) { - - if ( planes[ i ].distanceToPoint( point ) < 0 ) { - - return false; - - } - - } - - return true; - - } - - } ); - - /** - * Uniforms library for shared webgl shaders - */ - - var UniformsLib = { - - common: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - - map: { value: null }, - uvTransform: { value: new Matrix3() }, - uv2Transform: { value: new Matrix3() }, - - alphaMap: { value: null }, - - }, - - specularmap: { - - specularMap: { value: null }, - - }, - - envmap: { - - envMap: { value: null }, - flipEnvMap: { value: - 1 }, - reflectivity: { value: 1.0 }, - refractionRatio: { value: 0.98 }, - maxMipLevel: { value: 0 } - - }, - - aomap: { - - aoMap: { value: null }, - aoMapIntensity: { value: 1 } - - }, - - lightmap: { - - lightMap: { value: null }, - lightMapIntensity: { value: 1 } - - }, - - emissivemap: { - - emissiveMap: { value: null } - - }, - - bumpmap: { - - bumpMap: { value: null }, - bumpScale: { value: 1 } - - }, - - normalmap: { - - normalMap: { value: null }, - normalScale: { value: new Vector2( 1, 1 ) } - - }, - - displacementmap: { - - displacementMap: { value: null }, - displacementScale: { value: 1 }, - displacementBias: { value: 0 } - - }, - - roughnessmap: { - - roughnessMap: { value: null } - - }, - - metalnessmap: { - - metalnessMap: { value: null } - - }, - - gradientmap: { - - gradientMap: { value: null } - - }, - - fog: { - - fogDensity: { value: 0.00025 }, - fogNear: { value: 1 }, - fogFar: { value: 2000 }, - fogColor: { value: new Color( 0xffffff ) } - - }, - - lights: { - - ambientLightColor: { value: [] }, - - lightProbe: { value: [] }, - - directionalLights: { value: [], properties: { - direction: {}, - color: {} - } }, - - directionalLightShadows: { value: [], properties: { - shadowBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - directionalShadowMap: { value: [] }, - directionalShadowMatrix: { value: [] }, - - spotLights: { value: [], properties: { - color: {}, - position: {}, - direction: {}, - distance: {}, - coneCos: {}, - penumbraCos: {}, - decay: {} - } }, - - spotLightShadows: { value: [], properties: { - shadowBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - spotShadowMap: { value: [] }, - spotShadowMatrix: { value: [] }, - - pointLights: { value: [], properties: { - color: {}, - position: {}, - decay: {}, - distance: {} - } }, - - pointLightShadows: { value: [], properties: { - shadowBias: {}, - shadowRadius: {}, - shadowMapSize: {}, - shadowCameraNear: {}, - shadowCameraFar: {} - } }, - - pointShadowMap: { value: [] }, - pointShadowMatrix: { value: [] }, - - hemisphereLights: { value: [], properties: { - direction: {}, - skyColor: {}, - groundColor: {} - } }, - - // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src - rectAreaLights: { value: [], properties: { - color: {}, - position: {}, - width: {}, - height: {} - } } - - }, - - points: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - size: { value: 1.0 }, - scale: { value: 1.0 }, - map: { value: null }, - alphaMap: { value: null }, - uvTransform: { value: new Matrix3() } - - }, - - sprite: { - - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - center: { value: new Vector2( 0.5, 0.5 ) }, - rotation: { value: 0.0 }, - map: { value: null }, - alphaMap: { value: null }, - uvTransform: { value: new Matrix3() } - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLAnimation() { - - var context = null; - var isAnimating = false; - var animationLoop = null; - - function onAnimationFrame( time, frame ) { - - if ( isAnimating === false ) { return; } - - animationLoop( time, frame ); - - context.requestAnimationFrame( onAnimationFrame ); - - } - - return { - - start: function () { - - if ( isAnimating === true ) { return; } - if ( animationLoop === null ) { return; } - - context.requestAnimationFrame( onAnimationFrame ); - - isAnimating = true; - - }, - - stop: function () { - - isAnimating = false; - - }, - - setAnimationLoop: function ( callback ) { - - animationLoop = callback; - - }, - - setContext: function ( value ) { - - context = value; - - } - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLAttributes( gl, capabilities ) { - - var isWebGL2 = capabilities.isWebGL2; - - var buffers = new WeakMap(); - - function createBuffer( attribute, bufferType ) { - - var array = attribute.array; - var usage = attribute.usage; - - var buffer = gl.createBuffer(); - - gl.bindBuffer( bufferType, buffer ); - gl.bufferData( bufferType, array, usage ); - - attribute.onUploadCallback(); - - var type = 5126; - - if ( array instanceof Float32Array ) { - - type = 5126; - - } else if ( array instanceof Float64Array ) { - - console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' ); - - } else if ( array instanceof Uint16Array ) { - - type = 5123; - - } else if ( array instanceof Int16Array ) { - - type = 5122; - - } else if ( array instanceof Uint32Array ) { - - type = 5125; - - } else if ( array instanceof Int32Array ) { - - type = 5124; - - } else if ( array instanceof Int8Array ) { - - type = 5120; - - } else if ( array instanceof Uint8Array ) { - - type = 5121; - - } - - return { - buffer: buffer, - type: type, - bytesPerElement: array.BYTES_PER_ELEMENT, - version: attribute.version - }; - - } - - function updateBuffer( buffer, attribute, bufferType ) { - - var array = attribute.array; - var updateRange = attribute.updateRange; - - gl.bindBuffer( bufferType, buffer ); - - if ( updateRange.count === - 1 ) { - - // Not using update ranges - - gl.bufferSubData( bufferType, 0, array ); - - } else { - - if ( isWebGL2 ) { - - gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, - array, updateRange.offset, updateRange.count ); - - } else { - - gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, - array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); - - } - - updateRange.count = - 1; // reset range - - } - - } - - // - - function get( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) { attribute = attribute.data; } - - return buffers.get( attribute ); - - } - - function remove( attribute ) { - - if ( attribute.isInterleavedBufferAttribute ) { attribute = attribute.data; } - - var data = buffers.get( attribute ); - - if ( data ) { - - gl.deleteBuffer( data.buffer ); - - buffers.delete( attribute ); - - } - - } - - function update( attribute, bufferType ) { - - if ( attribute.isInterleavedBufferAttribute ) { attribute = attribute.data; } - - var data = buffers.get( attribute ); - - if ( data === undefined ) { - - buffers.set( attribute, createBuffer( attribute, bufferType ) ); - - } else if ( data.version < attribute.version ) { - - updateBuffer( data.buffer, attribute, bufferType ); - - data.version = attribute.version; - - } - - } - - return { - - get: get, - remove: remove, - update: update - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ - - // PlaneGeometry - - function PlaneGeometry( width, height, widthSegments, heightSegments ) { - - Geometry.call( this ); - - this.type = 'PlaneGeometry'; - - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; - - this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); - this.mergeVertices(); - - } - - PlaneGeometry.prototype = Object.create( Geometry.prototype ); - PlaneGeometry.prototype.constructor = PlaneGeometry; - - // PlaneBufferGeometry - - function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { - - BufferGeometry.call( this ); - - this.type = 'PlaneBufferGeometry'; - - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; - - width = width || 1; - height = height || 1; - - var width_half = width / 2; - var height_half = height / 2; - - var gridX = Math.floor( widthSegments ) || 1; - var gridY = Math.floor( heightSegments ) || 1; - - var gridX1 = gridX + 1; - var gridY1 = gridY + 1; - - var segment_width = width / gridX; - var segment_height = height / gridY; - - var ix, iy; - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - // generate vertices, normals and uvs - - for ( iy = 0; iy < gridY1; iy ++ ) { - - var y = iy * segment_height - height_half; - - for ( ix = 0; ix < gridX1; ix ++ ) { - - var x = ix * segment_width - width_half; - - vertices.push( x, - y, 0 ); - - normals.push( 0, 0, 1 ); - - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); - - } - - } - - // indices - - for ( iy = 0; iy < gridY; iy ++ ) { - - for ( ix = 0; ix < gridX; ix ++ ) { - - var a = ix + gridX1 * iy; - var b = ix + gridX1 * ( iy + 1 ); - var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); - var d = ( ix + 1 ) + gridX1 * iy; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry; - - var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif"; - - var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - - var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif"; - - var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif"; - - var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; - - var begin_vertex = "vec3 transformed = vec3( position );"; - - var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; - - var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif"; - - var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; - - var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; - - var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif"; - - var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif"; - - var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif"; - - var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; - - var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; - - var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; - - var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif"; - - var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n return m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; - - var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_maxMipLevel 8.0\n#define cubeUV_minMipLevel 4.0\n#define cubeUV_maxTileSize 256.0\n#define cubeUV_minTileSize 16.0\nfloat getFace(vec3 direction) {\n vec3 absDirection = abs(direction);\n float face = -1.0;\n if (absDirection.x > absDirection.z) {\n if (absDirection.x > absDirection.y)\n face = direction.x > 0.0 ? 0.0 : 3.0;\n else\n face = direction.y > 0.0 ? 1.0 : 4.0;\n } else {\n if (absDirection.z > absDirection.y)\n face = direction.z > 0.0 ? 2.0 : 5.0;\n else\n face = direction.y > 0.0 ? 1.0 : 4.0;\n }\n return face;\n}\nvec2 getUV(vec3 direction, float face) {\n vec2 uv;\n if (face == 0.0) {\n uv = vec2(direction.z, direction.y) / abs(direction.x); } else if (face == 1.0) {\n uv = vec2(-direction.x, -direction.z) / abs(direction.y); } else if (face == 2.0) {\n uv = vec2(-direction.x, direction.y) / abs(direction.z); } else if (face == 3.0) {\n uv = vec2(-direction.z, direction.y) / abs(direction.x); } else if (face == 4.0) {\n uv = vec2(-direction.x, direction.z) / abs(direction.y); } else {\n uv = vec2(direction.x, direction.y) / abs(direction.z); }\n return 0.5 * (uv + 1.0);\n}\nvec3 bilinearCubeUV(sampler2D envMap, vec3 direction, float mipInt) {\n float face = getFace(direction);\n float filterInt = max(cubeUV_minMipLevel - mipInt, 0.0);\n mipInt = max(mipInt, cubeUV_minMipLevel);\n float faceSize = exp2(mipInt);\n float texelSize = 1.0 / (3.0 * cubeUV_maxTileSize);\n vec2 uv = getUV(direction, face) * (faceSize - 1.0);\n vec2 f = fract(uv);\n uv += 0.5 - f;\n if (face > 2.0) {\n uv.y += faceSize;\n face -= 3.0;\n }\n uv.x += face * faceSize;\n if(mipInt < cubeUV_maxMipLevel){\n uv.y += 2.0 * cubeUV_maxTileSize;\n }\n uv.y += filterInt * 2.0 * cubeUV_minTileSize;\n uv.x += 3.0 * max(0.0, cubeUV_maxTileSize - 2.0 * faceSize);\n uv *= texelSize;\n vec3 tl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;\n uv.x += texelSize;\n vec3 tr = envMapTexelToLinear(texture2D(envMap, uv)).rgb;\n uv.y += texelSize;\n vec3 br = envMapTexelToLinear(texture2D(envMap, uv)).rgb;\n uv.x -= texelSize;\n vec3 bl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;\n vec3 tm = mix(tl, tr, f.x);\n vec3 bm = mix(bl, br, f.x);\n return mix(tm, bm, f.y);\n}\n#define r0 1.0\n#define v0 0.339\n#define m0 -2.0\n#define r1 0.8\n#define v1 0.276\n#define m1 -1.0\n#define r4 0.4\n#define v4 0.046\n#define m4 2.0\n#define r5 0.305\n#define v5 0.016\n#define m5 3.0\n#define r6 0.21\n#define v6 0.0038\n#define m6 4.0\nfloat roughnessToMip(float roughness) {\n float mip = 0.0;\n if (roughness >= r1) {\n mip = (r0 - roughness) * (m1 - m0) / (r0 - r1) + m0;\n } else if (roughness >= r4) {\n mip = (r1 - roughness) * (m4 - m1) / (r1 - r4) + m1;\n } else if (roughness >= r5) {\n mip = (r4 - roughness) * (m5 - m4) / (r4 - r5) + m4;\n } else if (roughness >= r6) {\n mip = (r5 - roughness) * (m6 - m5) / (r5 - r6) + m5;\n } else {\n mip = -2.0 * log2(1.16 * roughness); }\n return mip;\n}\nvec4 textureCubeUV(sampler2D envMap, vec3 sampleDir, float roughness) {\n float mip = clamp(roughnessToMip(roughness), m0, cubeUV_maxMipLevel);\n float mipF = fract(mip);\n float mipInt = floor(mip);\n vec3 color0 = bilinearCubeUV(envMap, sampleDir, mipInt);\n if (mipF == 0.0) {\n return vec4(color0, 1.0);\n } else {\n vec3 color1 = bilinearCubeUV(envMap, sampleDir, mipInt + 1.0);\n return vec4(mix(color0, color1, mipF), 1.0);\n }\n}\n#endif"; - - var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; - - var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif"; - - var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif"; - - var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; - - var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; - - var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; - - var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}"; - - var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\t\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec2 sampleUV = equirectUv( reflectVec );\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; - - var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; - - var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; - - var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif"; - - var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) { \n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; - - var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = -mvPosition.z;\n#endif"; - - var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif"; - - var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; - - var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; - - var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; - - var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif"; - - var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; - - var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; - - var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif"; - - var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t vec3 reflectVec = reflect( -viewDir, normal );\n\t\t reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t vec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV = equirectUv( reflectVec );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif"; - - var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; - - var lights_toon_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; - - var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; - - var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; - - var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif"; - - var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; - - var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; - - var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif"; - - var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; - - var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; - - var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif"; - - var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif"; - - var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif"; - - var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif"; - - var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; - - var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; - - var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - - var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif"; - - var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; - - var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif"; - - var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; - - var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; - - var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\tbitangent = bitangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; - - var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif"; - - var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tmat3 tsn = mat3( S, T, N );\n\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif"; - - var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; - - var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN );\n\t#endif\n#endif"; - - var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif"; - - var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}"; - - var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; - - var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;"; - - var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif"; - - var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif"; - - var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif"; - - var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; - - var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif"; - - var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif"; - - var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n#endif"; - - var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}"; - - var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; - - var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif"; - - var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif"; - - var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif"; - - var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; - - var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; - - var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; - - var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( ( color * ( 2.51 * color + 0.03 ) ) / ( color * ( 2.43 * color + 0.59 ) + 0.14 ) );\n}"; - - var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif"; - - var uv_pars_vertex = "#ifdef USE_UV\n\t#ifdef UVS_VERTEX_ONLY\n\t\tvec2 vUv;\n\t#else\n\t\tvarying vec2 vUv;\n\t#endif\n\tuniform mat3 uvTransform;\n#endif"; - - var uv_vertex = "#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif"; - - var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif"; - - var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n\tuniform mat3 uv2Transform;\n#endif"; - - var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif"; - - var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; - - var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - - var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; - - var cube_frag = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; - - var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; - - var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; - - var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; - - var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; - - var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; - - var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - - var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; - - var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; - - var meshtoon_frag = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshtoon_vert = "#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - - var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSPARENCY\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSPARENCY\n\tuniform float transparency;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSPARENCY\n\t\tdiffuseColor.a *= saturate( 1. - transparency + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - - var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; - - var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; - - var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; - - var shadow_vert = "#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - - var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}"; - - var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; - - var ShaderChunk = { - alphamap_fragment: alphamap_fragment, - alphamap_pars_fragment: alphamap_pars_fragment, - alphatest_fragment: alphatest_fragment, - aomap_fragment: aomap_fragment, - aomap_pars_fragment: aomap_pars_fragment, - begin_vertex: begin_vertex, - beginnormal_vertex: beginnormal_vertex, - bsdfs: bsdfs, - bumpmap_pars_fragment: bumpmap_pars_fragment, - clipping_planes_fragment: clipping_planes_fragment, - clipping_planes_pars_fragment: clipping_planes_pars_fragment, - clipping_planes_pars_vertex: clipping_planes_pars_vertex, - clipping_planes_vertex: clipping_planes_vertex, - color_fragment: color_fragment, - color_pars_fragment: color_pars_fragment, - color_pars_vertex: color_pars_vertex, - color_vertex: color_vertex, - common: common, - cube_uv_reflection_fragment: cube_uv_reflection_fragment, - defaultnormal_vertex: defaultnormal_vertex, - displacementmap_pars_vertex: displacementmap_pars_vertex, - displacementmap_vertex: displacementmap_vertex, - emissivemap_fragment: emissivemap_fragment, - emissivemap_pars_fragment: emissivemap_pars_fragment, - encodings_fragment: encodings_fragment, - encodings_pars_fragment: encodings_pars_fragment, - envmap_fragment: envmap_fragment, - envmap_common_pars_fragment: envmap_common_pars_fragment, - envmap_pars_fragment: envmap_pars_fragment, - envmap_pars_vertex: envmap_pars_vertex, - envmap_physical_pars_fragment: envmap_physical_pars_fragment, - envmap_vertex: envmap_vertex, - fog_vertex: fog_vertex, - fog_pars_vertex: fog_pars_vertex, - fog_fragment: fog_fragment, - fog_pars_fragment: fog_pars_fragment, - gradientmap_pars_fragment: gradientmap_pars_fragment, - lightmap_fragment: lightmap_fragment, - lightmap_pars_fragment: lightmap_pars_fragment, - lights_lambert_vertex: lights_lambert_vertex, - lights_pars_begin: lights_pars_begin, - lights_toon_fragment: lights_toon_fragment, - lights_toon_pars_fragment: lights_toon_pars_fragment, - lights_phong_fragment: lights_phong_fragment, - lights_phong_pars_fragment: lights_phong_pars_fragment, - lights_physical_fragment: lights_physical_fragment, - lights_physical_pars_fragment: lights_physical_pars_fragment, - lights_fragment_begin: lights_fragment_begin, - lights_fragment_maps: lights_fragment_maps, - lights_fragment_end: lights_fragment_end, - logdepthbuf_fragment: logdepthbuf_fragment, - logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, - logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, - logdepthbuf_vertex: logdepthbuf_vertex, - map_fragment: map_fragment, - map_pars_fragment: map_pars_fragment, - map_particle_fragment: map_particle_fragment, - map_particle_pars_fragment: map_particle_pars_fragment, - metalnessmap_fragment: metalnessmap_fragment, - metalnessmap_pars_fragment: metalnessmap_pars_fragment, - morphnormal_vertex: morphnormal_vertex, - morphtarget_pars_vertex: morphtarget_pars_vertex, - morphtarget_vertex: morphtarget_vertex, - normal_fragment_begin: normal_fragment_begin, - normal_fragment_maps: normal_fragment_maps, - normalmap_pars_fragment: normalmap_pars_fragment, - clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, - clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, - clearcoat_pars_fragment: clearcoat_pars_fragment, - packing: packing, - premultiplied_alpha_fragment: premultiplied_alpha_fragment, - project_vertex: project_vertex, - dithering_fragment: dithering_fragment, - dithering_pars_fragment: dithering_pars_fragment, - roughnessmap_fragment: roughnessmap_fragment, - roughnessmap_pars_fragment: roughnessmap_pars_fragment, - shadowmap_pars_fragment: shadowmap_pars_fragment, - shadowmap_pars_vertex: shadowmap_pars_vertex, - shadowmap_vertex: shadowmap_vertex, - shadowmask_pars_fragment: shadowmask_pars_fragment, - skinbase_vertex: skinbase_vertex, - skinning_pars_vertex: skinning_pars_vertex, - skinning_vertex: skinning_vertex, - skinnormal_vertex: skinnormal_vertex, - specularmap_fragment: specularmap_fragment, - specularmap_pars_fragment: specularmap_pars_fragment, - tonemapping_fragment: tonemapping_fragment, - tonemapping_pars_fragment: tonemapping_pars_fragment, - uv_pars_fragment: uv_pars_fragment, - uv_pars_vertex: uv_pars_vertex, - uv_vertex: uv_vertex, - uv2_pars_fragment: uv2_pars_fragment, - uv2_pars_vertex: uv2_pars_vertex, - uv2_vertex: uv2_vertex, - worldpos_vertex: worldpos_vertex, - - background_frag: background_frag, - background_vert: background_vert, - cube_frag: cube_frag, - cube_vert: cube_vert, - depth_frag: depth_frag, - depth_vert: depth_vert, - distanceRGBA_frag: distanceRGBA_frag, - distanceRGBA_vert: distanceRGBA_vert, - equirect_frag: equirect_frag, - equirect_vert: equirect_vert, - linedashed_frag: linedashed_frag, - linedashed_vert: linedashed_vert, - meshbasic_frag: meshbasic_frag, - meshbasic_vert: meshbasic_vert, - meshlambert_frag: meshlambert_frag, - meshlambert_vert: meshlambert_vert, - meshmatcap_frag: meshmatcap_frag, - meshmatcap_vert: meshmatcap_vert, - meshtoon_frag: meshtoon_frag, - meshtoon_vert: meshtoon_vert, - meshphong_frag: meshphong_frag, - meshphong_vert: meshphong_vert, - meshphysical_frag: meshphysical_frag, - meshphysical_vert: meshphysical_vert, - normal_frag: normal_frag, - normal_vert: normal_vert, - points_frag: points_frag, - points_vert: points_vert, - shadow_frag: shadow_frag, - shadow_vert: shadow_vert, - sprite_frag: sprite_frag, - sprite_vert: sprite_vert - }; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - */ - - var ShaderLib = { - - basic: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.meshbasic_vert, - fragmentShader: ShaderChunk.meshbasic_frag - - }, - - lambert: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) } - } - ] ), - - vertexShader: ShaderChunk.meshlambert_vert, - fragmentShader: ShaderChunk.meshlambert_frag - - }, - - phong: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - specular: { value: new Color( 0x111111 ) }, - shininess: { value: 30 } - } - ] ), - - vertexShader: ShaderChunk.meshphong_vert, - fragmentShader: ShaderChunk.meshphong_frag - - }, - - standard: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.roughnessmap, - UniformsLib.metalnessmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - roughness: { value: 1.0 }, - metalness: { value: 0.0 }, - envMapIntensity: { value: 1 } // temporary - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }, - - toon: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.gradientmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - specular: { value: new Color( 0x111111 ) }, - shininess: { value: 30 } - } - ] ), - - vertexShader: ShaderChunk.meshtoon_vert, - fragmentShader: ShaderChunk.meshtoon_frag - - }, - - matcap: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.fog, - { - matcap: { value: null } - } - ] ), - - vertexShader: ShaderChunk.meshmatcap_vert, - fragmentShader: ShaderChunk.meshmatcap_frag - - }, - - points: { - - uniforms: mergeUniforms( [ - UniformsLib.points, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.points_vert, - fragmentShader: ShaderChunk.points_frag - - }, - - dashed: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.fog, - { - scale: { value: 1 }, - dashSize: { value: 1 }, - totalSize: { value: 2 } - } - ] ), - - vertexShader: ShaderChunk.linedashed_vert, - fragmentShader: ShaderChunk.linedashed_frag - - }, - - depth: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap - ] ), - - vertexShader: ShaderChunk.depth_vert, - fragmentShader: ShaderChunk.depth_frag - - }, - - normal: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - { - opacity: { value: 1.0 } - } - ] ), - - vertexShader: ShaderChunk.normal_vert, - fragmentShader: ShaderChunk.normal_frag - - }, - - sprite: { - - uniforms: mergeUniforms( [ - UniformsLib.sprite, - UniformsLib.fog - ] ), - - vertexShader: ShaderChunk.sprite_vert, - fragmentShader: ShaderChunk.sprite_frag - - }, - - background: { - - uniforms: { - uvTransform: { value: new Matrix3() }, - t2D: { value: null }, - }, - - vertexShader: ShaderChunk.background_vert, - fragmentShader: ShaderChunk.background_frag - - }, - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ - - cube: { - - uniforms: mergeUniforms( [ - UniformsLib.envmap, - { - opacity: { value: 1.0 } - } - ] ), - - vertexShader: ShaderChunk.cube_vert, - fragmentShader: ShaderChunk.cube_frag - - }, - - equirect: { - - uniforms: { - tEquirect: { value: null }, - }, - - vertexShader: ShaderChunk.equirect_vert, - fragmentShader: ShaderChunk.equirect_frag - - }, - - distanceRGBA: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap, - { - referencePosition: { value: new Vector3() }, - nearDistance: { value: 1 }, - farDistance: { value: 1000 } - } - ] ), - - vertexShader: ShaderChunk.distanceRGBA_vert, - fragmentShader: ShaderChunk.distanceRGBA_frag - - }, - - shadow: { - - uniforms: mergeUniforms( [ - UniformsLib.lights, - UniformsLib.fog, - { - color: { value: new Color( 0x00000 ) }, - opacity: { value: 1.0 } - } ] ), - - vertexShader: ShaderChunk.shadow_vert, - fragmentShader: ShaderChunk.shadow_frag - - } - - }; - - ShaderLib.physical = { - - uniforms: mergeUniforms( [ - ShaderLib.standard.uniforms, - { - clearcoat: { value: 0 }, - clearcoatMap: { value: null }, - clearcoatRoughness: { value: 0 }, - clearcoatRoughnessMap: { value: null }, - clearcoatNormalScale: { value: new Vector2( 1, 1 ) }, - clearcoatNormalMap: { value: null }, - sheen: { value: new Color( 0x000000 ) }, - transparency: { value: 0 }, - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLBackground( renderer, state, objects, premultipliedAlpha ) { - - var clearColor = new Color( 0x000000 ); - var clearAlpha = 0; - - var planeMesh; - var boxMesh; - - var currentBackground = null; - var currentBackgroundVersion = 0; - var currentTonemapping = null; - - function render( renderList, scene, camera, forceClear ) { - - var background = scene.background; - - // Ignore background in AR - // TODO: Reconsider this. - - var xr = renderer.xr; - var session = xr.getSession && xr.getSession(); - - if ( session && session.environmentBlendMode === 'additive' ) { - - background = null; - - } - - if ( background === null ) { - - setClear( clearColor, clearAlpha ); - - } else if ( background && background.isColor ) { - - setClear( background, 1 ); - forceClear = true; - - } - - if ( renderer.autoClear || forceClear ) { - - renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - - } - - if ( background && ( background.isCubeTexture || background.isWebGLCubeRenderTarget || background.mapping === CubeUVReflectionMapping ) ) { - - if ( boxMesh === undefined ) { - - boxMesh = new Mesh( - new BoxBufferGeometry( 1, 1, 1 ), - new ShaderMaterial( { - type: 'BackgroundCubeMaterial', - uniforms: cloneUniforms( ShaderLib.cube.uniforms ), - vertexShader: ShaderLib.cube.vertexShader, - fragmentShader: ShaderLib.cube.fragmentShader, - side: BackSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); - - boxMesh.geometry.deleteAttribute( 'normal' ); - boxMesh.geometry.deleteAttribute( 'uv' ); - - boxMesh.onBeforeRender = function ( renderer, scene, camera ) { - - this.matrixWorld.copyPosition( camera.matrixWorld ); - - }; - - // enable code injection for non-built-in material - Object.defineProperty( boxMesh.material, 'envMap', { - - get: function () { - - return this.uniforms.envMap.value; - - } - - } ); - - objects.update( boxMesh ); - - } - - var texture = background.isWebGLCubeRenderTarget ? background.texture : background; - - boxMesh.material.uniforms.envMap.value = texture; - boxMesh.material.uniforms.flipEnvMap.value = texture.isCubeTexture ? - 1 : 1; - - if ( currentBackground !== background || - currentBackgroundVersion !== texture.version || - currentTonemapping !== renderer.toneMapping ) { - - boxMesh.material.needsUpdate = true; - - currentBackground = background; - currentBackgroundVersion = texture.version; - currentTonemapping = renderer.toneMapping; - - } - - // push to the pre-sorted opaque render list - renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); - - } else if ( background && background.isTexture ) { - - if ( planeMesh === undefined ) { - - planeMesh = new Mesh( - new PlaneBufferGeometry( 2, 2 ), - new ShaderMaterial( { - type: 'BackgroundMaterial', - uniforms: cloneUniforms( ShaderLib.background.uniforms ), - vertexShader: ShaderLib.background.vertexShader, - fragmentShader: ShaderLib.background.fragmentShader, - side: FrontSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); - - planeMesh.geometry.deleteAttribute( 'normal' ); - - // enable code injection for non-built-in material - Object.defineProperty( planeMesh.material, 'map', { - - get: function () { - - return this.uniforms.t2D.value; - - } - - } ); - - objects.update( planeMesh ); - - } - - planeMesh.material.uniforms.t2D.value = background; - - if ( background.matrixAutoUpdate === true ) { - - background.updateMatrix(); - - } - - planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); - - if ( currentBackground !== background || - currentBackgroundVersion !== background.version || - currentTonemapping !== renderer.toneMapping ) { - - planeMesh.material.needsUpdate = true; - - currentBackground = background; - currentBackgroundVersion = background.version; - currentTonemapping = renderer.toneMapping; - - } - - - // push to the pre-sorted opaque render list - renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); - - } - - } - - function setClear( color, alpha ) { - - state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); - - } - - return { - - getClearColor: function () { - - return clearColor; - - }, - setClearColor: function ( color, alpha ) { - - clearColor.set( color ); - clearAlpha = alpha !== undefined ? alpha : 1; - setClear( clearColor, clearAlpha ); - - }, - getClearAlpha: function () { - - return clearAlpha; - - }, - setClearAlpha: function ( alpha ) { - - clearAlpha = alpha; - setClear( clearColor, clearAlpha ); - - }, - render: render - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLBufferRenderer( gl, extensions, info, capabilities ) { - - var isWebGL2 = capabilities.isWebGL2; - - var mode; - - function setMode( value ) { - - mode = value; - - } - - function render( start, count ) { - - gl.drawArrays( mode, start, count ); - - info.update( count, mode ); - - } - - function renderInstances( geometry, start, count, primcount ) { - - if ( primcount === 0 ) { return; } - - var extension, methodName; - - if ( isWebGL2 ) { - - extension = gl; - methodName = 'drawArraysInstanced'; - - } else { - - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawArraysInstancedANGLE'; - - if ( extension === null ) { - - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - } - - extension[ methodName ]( mode, start, count, primcount ); - - info.update( count, mode, primcount ); - - } - - // - - this.setMode = setMode; - this.render = render; - this.renderInstances = renderInstances; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLCapabilities( gl, extensions, parameters ) { - - var maxAnisotropy; - - function getMaxAnisotropy() { - - if ( maxAnisotropy !== undefined ) { return maxAnisotropy; } - - var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( extension !== null ) { - - maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); - - } else { - - maxAnisotropy = 0; - - } - - return maxAnisotropy; - - } - - function getMaxPrecision( precision ) { - - if ( precision === 'highp' ) { - - if ( gl.getShaderPrecisionFormat( 35633, 36338 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36338 ).precision > 0 ) { - - return 'highp'; - - } - - precision = 'mediump'; - - } - - if ( precision === 'mediump' ) { - - if ( gl.getShaderPrecisionFormat( 35633, 36337 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36337 ).precision > 0 ) { - - return 'mediump'; - - } - - } - - return 'lowp'; - - } - - /* eslint-disable no-undef */ - var isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext ) || - ( typeof WebGL2ComputeRenderingContext !== 'undefined' && gl instanceof WebGL2ComputeRenderingContext ); - /* eslint-enable no-undef */ - - var precision = parameters.precision !== undefined ? parameters.precision : 'highp'; - var maxPrecision = getMaxPrecision( precision ); - - if ( maxPrecision !== precision ) { - - console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); - precision = maxPrecision; - - } - - var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; - - var maxTextures = gl.getParameter( 34930 ); - var maxVertexTextures = gl.getParameter( 35660 ); - var maxTextureSize = gl.getParameter( 3379 ); - var maxCubemapSize = gl.getParameter( 34076 ); - - var maxAttributes = gl.getParameter( 34921 ); - var maxVertexUniforms = gl.getParameter( 36347 ); - var maxVaryings = gl.getParameter( 36348 ); - var maxFragmentUniforms = gl.getParameter( 36349 ); - - var vertexTextures = maxVertexTextures > 0; - var floatFragmentTextures = isWebGL2 || !! extensions.get( 'OES_texture_float' ); - var floatVertexTextures = vertexTextures && floatFragmentTextures; - - var maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0; - - return { - - isWebGL2: isWebGL2, - - getMaxAnisotropy: getMaxAnisotropy, - getMaxPrecision: getMaxPrecision, - - precision: precision, - logarithmicDepthBuffer: logarithmicDepthBuffer, - - maxTextures: maxTextures, - maxVertexTextures: maxVertexTextures, - maxTextureSize: maxTextureSize, - maxCubemapSize: maxCubemapSize, - - maxAttributes: maxAttributes, - maxVertexUniforms: maxVertexUniforms, - maxVaryings: maxVaryings, - maxFragmentUniforms: maxFragmentUniforms, - - vertexTextures: vertexTextures, - floatFragmentTextures: floatFragmentTextures, - floatVertexTextures: floatVertexTextures, - - maxSamples: maxSamples - - }; - - } - - /** - * @author tschw - */ - - function WebGLClipping() { - - var scope = this, - - globalState = null, - numGlobalPlanes = 0, - localClippingEnabled = false, - renderingShadows = false, - - plane = new Plane(), - viewNormalMatrix = new Matrix3(), - - uniform = { value: null, needsUpdate: false }; - - this.uniform = uniform; - this.numPlanes = 0; - this.numIntersection = 0; - - this.init = function ( planes, enableLocalClipping, camera ) { - - var enabled = - planes.length !== 0 || - enableLocalClipping || - // enable state of previous frame - the clipping code has to - // run another frame in order to reset the state: - numGlobalPlanes !== 0 || - localClippingEnabled; - - localClippingEnabled = enableLocalClipping; - - globalState = projectPlanes( planes, camera, 0 ); - numGlobalPlanes = planes.length; - - return enabled; - - }; - - this.beginShadows = function () { - - renderingShadows = true; - projectPlanes( null ); - - }; - - this.endShadows = function () { - - renderingShadows = false; - resetGlobalState(); - - }; - - this.setState = function ( planes, clipIntersection, clipShadows, camera, cache, fromCache ) { - - if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { - - // there's no local clipping - - if ( renderingShadows ) { - - // there's no global clipping - - projectPlanes( null ); - - } else { - - resetGlobalState(); - - } - - } else { - - var nGlobal = renderingShadows ? 0 : numGlobalPlanes, - lGlobal = nGlobal * 4, - - dstArray = cache.clippingState || null; - - uniform.value = dstArray; // ensure unique state - - dstArray = projectPlanes( planes, camera, lGlobal, fromCache ); - - for ( var i = 0; i !== lGlobal; ++ i ) { - - dstArray[ i ] = globalState[ i ]; - - } - - cache.clippingState = dstArray; - this.numIntersection = clipIntersection ? this.numPlanes : 0; - this.numPlanes += nGlobal; - - } - - - }; - - function resetGlobalState() { - - if ( uniform.value !== globalState ) { - - uniform.value = globalState; - uniform.needsUpdate = numGlobalPlanes > 0; - - } - - scope.numPlanes = numGlobalPlanes; - scope.numIntersection = 0; - - } - - function projectPlanes( planes, camera, dstOffset, skipTransform ) { - - var nPlanes = planes !== null ? planes.length : 0, - dstArray = null; - - if ( nPlanes !== 0 ) { - - dstArray = uniform.value; - - if ( skipTransform !== true || dstArray === null ) { - - var flatSize = dstOffset + nPlanes * 4, - viewMatrix = camera.matrixWorldInverse; - - viewNormalMatrix.getNormalMatrix( viewMatrix ); - - if ( dstArray === null || dstArray.length < flatSize ) { - - dstArray = new Float32Array( flatSize ); - - } - - for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { - - plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); - - plane.normal.toArray( dstArray, i4 ); - dstArray[ i4 + 3 ] = plane.constant; - - } - - } - - uniform.value = dstArray; - uniform.needsUpdate = true; - - } - - scope.numPlanes = nPlanes; - scope.numIntersection = 0; - - return dstArray; - - } - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLExtensions( gl ) { - - var extensions = {}; - - return { - - get: function ( name ) { - - if ( extensions[ name ] !== undefined ) { - - return extensions[ name ]; - - } - - var extension; - - switch ( name ) { - - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; - - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; - - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; - - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; - - default: - extension = gl.getExtension( name ); - - } - - if ( extension === null ) { - - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - - } - - extensions[ name ] = extension; - - return extension; - - } - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLGeometries( gl, attributes, info ) { - - var geometries = new WeakMap(); - var wireframeAttributes = new WeakMap(); - - function onGeometryDispose( event ) { - - var geometry = event.target; - var buffergeometry = geometries.get( geometry ); - - if ( buffergeometry.index !== null ) { - - attributes.remove( buffergeometry.index ); - - } - - for ( var name in buffergeometry.attributes ) { - - attributes.remove( buffergeometry.attributes[ name ] ); - - } - - geometry.removeEventListener( 'dispose', onGeometryDispose ); - - geometries.delete( geometry ); - - var attribute = wireframeAttributes.get( buffergeometry ); - - if ( attribute ) { - - attributes.remove( attribute ); - wireframeAttributes.delete( buffergeometry ); - - } - - // - - info.memory.geometries --; - - } - - function get( object, geometry ) { - - var buffergeometry = geometries.get( geometry ); - - if ( buffergeometry ) { return buffergeometry; } - - geometry.addEventListener( 'dispose', onGeometryDispose ); - - if ( geometry.isBufferGeometry ) { - - buffergeometry = geometry; - - } else if ( geometry.isGeometry ) { - - if ( geometry._bufferGeometry === undefined ) { - - geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); - - } - - buffergeometry = geometry._bufferGeometry; - - } - - geometries.set( geometry, buffergeometry ); - - info.memory.geometries ++; - - return buffergeometry; - - } - - function update( geometry ) { - - var index = geometry.index; - var geometryAttributes = geometry.attributes; - - if ( index !== null ) { - - attributes.update( index, 34963 ); - - } - - for ( var name in geometryAttributes ) { - - attributes.update( geometryAttributes[ name ], 34962 ); - - } - - // morph targets - - var morphAttributes = geometry.morphAttributes; - - for ( var name in morphAttributes ) { - - var array = morphAttributes[ name ]; - - for ( var i = 0, l = array.length; i < l; i ++ ) { - - attributes.update( array[ i ], 34962 ); - - } - - } - - } - - function updateWireframeAttribute( geometry ) { - - var indices = []; - - var geometryIndex = geometry.index; - var geometryPosition = geometry.attributes.position; - var version = 0; - - if ( geometryIndex !== null ) { - - var array = geometryIndex.array; - version = geometryIndex.version; - - for ( var i = 0, l = array.length; i < l; i += 3 ) { - - var a = array[ i + 0 ]; - var b = array[ i + 1 ]; - var c = array[ i + 2 ]; - - indices.push( a, b, b, c, c, a ); - - } - - } else { - - var array = geometryPosition.array; - version = geometryPosition.version; - - for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - - var a = i + 0; - var b = i + 1; - var c = i + 2; - - indices.push( a, b, b, c, c, a ); - - } - - } - - var attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); - attribute.version = version; - - attributes.update( attribute, 34963 ); - - // - - var previousAttribute = wireframeAttributes.get( geometry ); - - if ( previousAttribute ) { attributes.remove( previousAttribute ); } - - // - - wireframeAttributes.set( geometry, attribute ); - - } - - function getWireframeAttribute( geometry ) { - - var currentAttribute = wireframeAttributes.get( geometry ); - - if ( currentAttribute ) { - - var geometryIndex = geometry.index; - - if ( geometryIndex !== null ) { - - // if the attribute is obsolete, create a new one - - if ( currentAttribute.version < geometryIndex.version ) { - - updateWireframeAttribute( geometry ); - - } - - } - - } else { - - updateWireframeAttribute( geometry ); - - } - - return wireframeAttributes.get( geometry ); - - } - - return { - - get: get, - update: update, - - getWireframeAttribute: getWireframeAttribute - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { - - var isWebGL2 = capabilities.isWebGL2; - - var mode; - - function setMode( value ) { - - mode = value; - - } - - var type, bytesPerElement; - - function setIndex( value ) { - - type = value.type; - bytesPerElement = value.bytesPerElement; - - } - - function render( start, count ) { - - gl.drawElements( mode, count, type, start * bytesPerElement ); - - info.update( count, mode ); - - } - - function renderInstances( geometry, start, count, primcount ) { - - if ( primcount === 0 ) { return; } - - var extension, methodName; - - if ( isWebGL2 ) { - - extension = gl; - methodName = 'drawElementsInstanced'; - - } else { - - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawElementsInstancedANGLE'; - - if ( extension === null ) { - - console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; - - } - - } - - extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); - - info.update( count, mode, primcount ); - - } - - // - - this.setMode = setMode; - this.setIndex = setIndex; - this.render = render; - this.renderInstances = renderInstances; - - } - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function WebGLInfo( gl ) { - - var memory = { - geometries: 0, - textures: 0 - }; - - var render = { - frame: 0, - calls: 0, - triangles: 0, - points: 0, - lines: 0 - }; - - function update( count, mode, instanceCount ) { - - instanceCount = instanceCount || 1; - - render.calls ++; - - switch ( mode ) { - - case 4: - render.triangles += instanceCount * ( count / 3 ); - break; - - case 1: - render.lines += instanceCount * ( count / 2 ); - break; - - case 3: - render.lines += instanceCount * ( count - 1 ); - break; - - case 2: - render.lines += instanceCount * count; - break; - - case 0: - render.points += instanceCount * count; - break; - - default: - console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); - break; - - } - - } - - function reset() { - - render.frame ++; - render.calls = 0; - render.triangles = 0; - render.points = 0; - render.lines = 0; - - } - - return { - memory: memory, - render: render, - programs: null, - autoReset: true, - reset: reset, - update: update - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function absNumericalSort( a, b ) { - - return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); - - } - - function WebGLMorphtargets( gl ) { - - var influencesList = {}; - var morphInfluences = new Float32Array( 8 ); - - function update( object, geometry, material, program ) { - - var objectInfluences = object.morphTargetInfluences; - - // When object doesn't have morph target influences defined, we treat it as a 0-length array - // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences - - var length = objectInfluences === undefined ? 0 : objectInfluences.length; - - var influences = influencesList[ geometry.id ]; - - if ( influences === undefined ) { - - // initialise list - - influences = []; - - for ( var i = 0; i < length; i ++ ) { - - influences[ i ] = [ i, 0 ]; - - } - - influencesList[ geometry.id ] = influences; - - } - - var morphTargets = material.morphTargets && geometry.morphAttributes.position; - var morphNormals = material.morphNormals && geometry.morphAttributes.normal; - - // Remove current morphAttributes - - for ( var i = 0; i < length; i ++ ) { - - var influence = influences[ i ]; - - if ( influence[ 1 ] !== 0 ) { - - if ( morphTargets ) { geometry.deleteAttribute( 'morphTarget' + i ); } - if ( morphNormals ) { geometry.deleteAttribute( 'morphNormal' + i ); } - - } - - } - - // Collect influences - - for ( var i = 0; i < length; i ++ ) { - - var influence = influences[ i ]; - - influence[ 0 ] = i; - influence[ 1 ] = objectInfluences[ i ]; - - } - - influences.sort( absNumericalSort ); - - // Add morphAttributes - - var morphInfluencesSum = 0; - - for ( var i = 0; i < 8; i ++ ) { - - var influence = influences[ i ]; - - if ( influence ) { - - var index = influence[ 0 ]; - var value = influence[ 1 ]; - - if ( value ) { - - if ( morphTargets ) { geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); } - if ( morphNormals ) { geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); } - - morphInfluences[ i ] = value; - morphInfluencesSum += value; - continue; - - } - - } - - morphInfluences[ i ] = 0; - - } - - // GLSL shader uses formula baseinfluence * base + sum(target * influence) - // This allows us to switch between absolute morphs and relative morphs without changing shader code - // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) - var morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - - program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); - program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); - - } - - return { - - update: update - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLObjects( gl, geometries, attributes, info ) { - - var updateMap = new WeakMap(); - - function update( object ) { - - var frame = info.render.frame; - - var geometry = object.geometry; - var buffergeometry = geometries.get( object, geometry ); - - // Update once per frame - - if ( updateMap.get( buffergeometry ) !== frame ) { - - if ( geometry.isGeometry ) { - - buffergeometry.updateFromObject( object ); - - } - - geometries.update( buffergeometry ); - - updateMap.set( buffergeometry, frame ); - - } - - if ( object.isInstancedMesh ) { - - attributes.update( object.instanceMatrix, 34962 ); - - } - - return buffergeometry; - - } - - function dispose() { - - updateMap = new WeakMap(); - - } - - return { - - update: update, - dispose: dispose - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - - images = images !== undefined ? images : []; - mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - format = format !== undefined ? format : RGBFormat; - - Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.flipY = false; - - } - - CubeTexture.prototype = Object.create( Texture.prototype ); - CubeTexture.prototype.constructor = CubeTexture; - - CubeTexture.prototype.isCubeTexture = true; - - Object.defineProperty( CubeTexture.prototype, 'images', { - - get: function () { - - return this.image; - - }, - - set: function ( value ) { - - this.image = value; - - } - - } ); - - /** - * @author Takahiro https://github.com/takahirox - */ - - function DataTexture2DArray( data, width, height, depth ) { - - Texture.call( this, null ); - - this.image = { data: data || null, width: width || 1, height: height || 1, depth: depth || 1 }; - - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; - - this.wrapR = ClampToEdgeWrapping; - - this.generateMipmaps = false; - this.flipY = false; - - this.needsUpdate = true; - - } - - DataTexture2DArray.prototype = Object.create( Texture.prototype ); - DataTexture2DArray.prototype.constructor = DataTexture2DArray; - DataTexture2DArray.prototype.isDataTexture2DArray = true; - - /** - * @author Artur Trzesiok - */ - - function DataTexture3D( data, width, height, depth ) { - - // We're going to add .setXXX() methods for setting properties later. - // Users can still set in DataTexture3D directly. - // - // var texture = new THREE.DataTexture3D( data, width, height, depth ); - // texture.anisotropy = 16; - // - // See #14839 - - Texture.call( this, null ); - - this.image = { data: data || null, width: width || 1, height: height || 1, depth: depth || 1 }; - - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; - - this.wrapR = ClampToEdgeWrapping; - - this.generateMipmaps = false; - this.flipY = false; - - this.needsUpdate = true; - - - } - - DataTexture3D.prototype = Object.create( Texture.prototype ); - DataTexture3D.prototype.constructor = DataTexture3D; - DataTexture3D.prototype.isDataTexture3D = true; - - /** - * @author tschw - * @author Mugen87 / https://github.com/Mugen87 - * @author mrdoob / http://mrdoob.com/ - * - * Uniforms of a program. - * Those form a tree structure with a special top-level container for the root, - * which you get by calling 'new WebGLUniforms( gl, program )'. - * - * - * Properties of inner nodes including the top-level container: - * - * .seq - array of nested uniforms - * .map - nested uniforms by name - * - * - * Methods of all nodes except the top-level container: - * - * .setValue( gl, value, [textures] ) - * - * uploads a uniform value(s) - * the 'textures' parameter is needed for sampler uniforms - * - * - * Static methods of the top-level container (textures factorizations): - * - * .upload( gl, seq, values, textures ) - * - * sets uniforms in 'seq' to 'values[id].value' - * - * .seqWithValue( seq, values ) : filteredSeq - * - * filters 'seq' entries with corresponding entry in values - * - * - * Methods of the top-level container (textures factorizations): - * - * .setValue( gl, name, value, textures ) - * - * sets uniform with name 'name' to 'value' - * - * .setOptional( gl, obj, prop ) - * - * like .set for an optional property of the object - * - */ - - var emptyTexture = new Texture(); - var emptyTexture2dArray = new DataTexture2DArray(); - var emptyTexture3d = new DataTexture3D(); - var emptyCubeTexture = new CubeTexture(); - - // --- Utilities --- - - // Array Caches (provide typed arrays for temporary by size) - - var arrayCacheF32 = []; - var arrayCacheI32 = []; - - // Float32Array caches used for uploading Matrix uniforms - - var mat4array = new Float32Array( 16 ); - var mat3array = new Float32Array( 9 ); - var mat2array = new Float32Array( 4 ); - - // Flattening for arrays of vectors and matrices - - function flatten( array, nBlocks, blockSize ) { - - var firstElem = array[ 0 ]; - - if ( firstElem <= 0 || firstElem > 0 ) { return array; } - // unoptimized: ! isNaN( firstElem ) - // see http://jacksondunstan.com/articles/983 - - var n = nBlocks * blockSize, - r = arrayCacheF32[ n ]; - - if ( r === undefined ) { - - r = new Float32Array( n ); - arrayCacheF32[ n ] = r; - - } - - if ( nBlocks !== 0 ) { - - firstElem.toArray( r, 0 ); - - for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) { - - offset += blockSize; - array[ i ].toArray( r, offset ); - - } - - } - - return r; - - } - - function arraysEqual( a, b ) { - - if ( a.length !== b.length ) { return false; } - - for ( var i = 0, l = a.length; i < l; i ++ ) { - - if ( a[ i ] !== b[ i ] ) { return false; } - - } - - return true; - - } - - function copyArray( a, b ) { - - for ( var i = 0, l = b.length; i < l; i ++ ) { - - a[ i ] = b[ i ]; - - } - - } - - // Texture unit allocation - - function allocTexUnits( textures, n ) { - - var r = arrayCacheI32[ n ]; - - if ( r === undefined ) { - - r = new Int32Array( n ); - arrayCacheI32[ n ] = r; - - } - - for ( var i = 0; i !== n; ++ i ) - { r[ i ] = textures.allocateTextureUnit(); } - - return r; - - } - - // --- Setters --- - - // Note: Defining these methods externally, because they come in a bunch - // and this way their names minify. - - // Single scalar - - function setValueV1f( gl, v ) { - - var cache = this.cache; - - if ( cache[ 0 ] === v ) { return; } - - gl.uniform1f( this.addr, v ); - - cache[ 0 ] = v; - - } - - // Single float vector (from flat array or THREE.VectorN) - - function setValueV2f( gl, v ) { - - var cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - - gl.uniform2f( this.addr, v.x, v.y ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - - } - - } else { - - if ( arraysEqual( cache, v ) ) { return; } - - gl.uniform2fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - function setValueV3f( gl, v ) { - - var cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - - gl.uniform3f( this.addr, v.x, v.y, v.z ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - - } - - } else if ( v.r !== undefined ) { - - if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { - - gl.uniform3f( this.addr, v.r, v.g, v.b ); - - cache[ 0 ] = v.r; - cache[ 1 ] = v.g; - cache[ 2 ] = v.b; - - } - - } else { - - if ( arraysEqual( cache, v ) ) { return; } - - gl.uniform3fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - function setValueV4f( gl, v ) { - - var cache = this.cache; - - if ( v.x !== undefined ) { - - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - - gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - cache[ 3 ] = v.w; - - } - - } else { - - if ( arraysEqual( cache, v ) ) { return; } - - gl.uniform4fv( this.addr, v ); - - copyArray( cache, v ); - - } - - } - - // Single matrix (from flat array or MatrixN) - - function setValueM2( gl, v ) { - - var cache = this.cache; - var elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) { return; } - - gl.uniformMatrix2fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) { return; } - - mat2array.set( elements ); - - gl.uniformMatrix2fv( this.addr, false, mat2array ); - - copyArray( cache, elements ); - - } - - } - - function setValueM3( gl, v ) { - - var cache = this.cache; - var elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) { return; } - - gl.uniformMatrix3fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) { return; } - - mat3array.set( elements ); - - gl.uniformMatrix3fv( this.addr, false, mat3array ); - - copyArray( cache, elements ); - - } - - } - - function setValueM4( gl, v ) { - - var cache = this.cache; - var elements = v.elements; - - if ( elements === undefined ) { - - if ( arraysEqual( cache, v ) ) { return; } - - gl.uniformMatrix4fv( this.addr, false, v ); - - copyArray( cache, v ); - - } else { - - if ( arraysEqual( cache, elements ) ) { return; } - - mat4array.set( elements ); - - gl.uniformMatrix4fv( this.addr, false, mat4array ); - - copyArray( cache, elements ); - - } - - } - - // Single texture (2D / Cube) - - function setValueT1( gl, v, textures ) { - - var cache = this.cache; - var unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.safeSetTexture2D( v || emptyTexture, unit ); - - } - - function setValueT2DArray1( gl, v, textures ) { - - var cache = this.cache; - var unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.setTexture2DArray( v || emptyTexture2dArray, unit ); - - } - - function setValueT3D1( gl, v, textures ) { - - var cache = this.cache; - var unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.setTexture3D( v || emptyTexture3d, unit ); - - } - - function setValueT6( gl, v, textures ) { - - var cache = this.cache; - var unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; - - } - - textures.safeSetTextureCube( v || emptyCubeTexture, unit ); - - } - - // Integer / Boolean vectors or arrays thereof (always flat arrays) - - function setValueV1i( gl, v ) { - - var cache = this.cache; - - if ( cache[ 0 ] === v ) { return; } - - gl.uniform1i( this.addr, v ); - - cache[ 0 ] = v; - - } - - function setValueV2i( gl, v ) { - - var cache = this.cache; - - if ( arraysEqual( cache, v ) ) { return; } - - gl.uniform2iv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV3i( gl, v ) { - - var cache = this.cache; - - if ( arraysEqual( cache, v ) ) { return; } - - gl.uniform3iv( this.addr, v ); - - copyArray( cache, v ); - - } - - function setValueV4i( gl, v ) { - - var cache = this.cache; - - if ( arraysEqual( cache, v ) ) { return; } - - gl.uniform4iv( this.addr, v ); - - copyArray( cache, v ); - - } - - // uint - - function setValueV1ui( gl, v ) { - - var cache = this.cache; - - if ( cache[ 0 ] === v ) { return; } - - gl.uniform1ui( this.addr, v ); - - cache[ 0 ] = v; - - } - - // Helper to pick the right setter for the singular case - - function getSingularSetter( type ) { - - switch ( type ) { - - case 0x1406: return setValueV1f; // FLOAT - case 0x8b50: return setValueV2f; // _VEC2 - case 0x8b51: return setValueV3f; // _VEC3 - case 0x8b52: return setValueV4f; // _VEC4 - - case 0x8b5a: return setValueM2; // _MAT2 - case 0x8b5b: return setValueM3; // _MAT3 - case 0x8b5c: return setValueM4; // _MAT4 - - case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 - - case 0x1405: return setValueV1ui; // UINT - - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1; - - case 0x8b5f: // SAMPLER_3D - case 0x8dcb: // INT_SAMPLER_3D - case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D - return setValueT3D1; - - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6; - - case 0x8dc1: // SAMPLER_2D_ARRAY - case 0x8dcf: // INT_SAMPLER_2D_ARRAY - case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY - case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW - return setValueT2DArray1; - - } - - } - - // Array of scalars - function setValueV1fArray( gl, v ) { - - gl.uniform1fv( this.addr, v ); - - } - - // Integer / Boolean vectors or arrays thereof (always flat arrays) - function setValueV1iArray( gl, v ) { - - gl.uniform1iv( this.addr, v ); - - } - - function setValueV2iArray( gl, v ) { - - gl.uniform2iv( this.addr, v ); - - } - - function setValueV3iArray( gl, v ) { - - gl.uniform3iv( this.addr, v ); - - } - - function setValueV4iArray( gl, v ) { - - gl.uniform4iv( this.addr, v ); - - } - - - // Array of vectors (flat or from THREE classes) - - function setValueV2fArray( gl, v ) { - - var data = flatten( v, this.size, 2 ); - - gl.uniform2fv( this.addr, data ); - - } - - function setValueV3fArray( gl, v ) { - - var data = flatten( v, this.size, 3 ); - - gl.uniform3fv( this.addr, data ); - - } - - function setValueV4fArray( gl, v ) { - - var data = flatten( v, this.size, 4 ); - - gl.uniform4fv( this.addr, data ); - - } - - // Array of matrices (flat or from THREE clases) - - function setValueM2Array( gl, v ) { - - var data = flatten( v, this.size, 4 ); - - gl.uniformMatrix2fv( this.addr, false, data ); - - } - - function setValueM3Array( gl, v ) { - - var data = flatten( v, this.size, 9 ); - - gl.uniformMatrix3fv( this.addr, false, data ); - - } - - function setValueM4Array( gl, v ) { - - var data = flatten( v, this.size, 16 ); - - gl.uniformMatrix4fv( this.addr, false, data ); - - } - - // Array of textures (2D / Cube) - - function setValueT1Array( gl, v, textures ) { - - var n = v.length; - - var units = allocTexUnits( textures, n ); - - gl.uniform1iv( this.addr, units ); - - for ( var i = 0; i !== n; ++ i ) { - - textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); - - } - - } - - function setValueT6Array( gl, v, textures ) { - - var n = v.length; - - var units = allocTexUnits( textures, n ); - - gl.uniform1iv( this.addr, units ); - - for ( var i = 0; i !== n; ++ i ) { - - textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); - - } - - } - - // Helper to pick the right setter for a pure (bottom-level) array - - function getPureArraySetter( type ) { - - switch ( type ) { - - case 0x1406: return setValueV1fArray; // FLOAT - case 0x8b50: return setValueV2fArray; // _VEC2 - case 0x8b51: return setValueV3fArray; // _VEC3 - case 0x8b52: return setValueV4fArray; // _VEC4 - - case 0x8b5a: return setValueM2Array; // _MAT2 - case 0x8b5b: return setValueM3Array; // _MAT3 - case 0x8b5c: return setValueM4Array; // _MAT4 - - case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 - - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1Array; - - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6Array; - - } - - } - - // --- Uniform Classes --- - - function SingleUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.cache = []; - this.setValue = getSingularSetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - function PureArrayUniform( id, activeInfo, addr ) { - - this.id = id; - this.addr = addr; - this.cache = []; - this.size = activeInfo.size; - this.setValue = getPureArraySetter( activeInfo.type ); - - // this.path = activeInfo.name; // DEBUG - - } - - PureArrayUniform.prototype.updateCache = function ( data ) { - - var cache = this.cache; - - if ( data instanceof Float32Array && cache.length !== data.length ) { - - this.cache = new Float32Array( data.length ); - - } - - copyArray( cache, data ); - - }; - - function StructuredUniform( id ) { - - this.id = id; - - this.seq = []; - this.map = {}; - - } - - StructuredUniform.prototype.setValue = function ( gl, value, textures ) { - - var seq = this.seq; - - for ( var i = 0, n = seq.length; i !== n; ++ i ) { - - var u = seq[ i ]; - u.setValue( gl, value[ u.id ], textures ); - - } - - }; - - // --- Top-level --- - - // Parser - builds up the property tree from the path strings - - var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g; - - // extracts - // - the identifier (member name or array index) - // - followed by an optional right bracket (found when array index) - // - followed by an optional left bracket or dot (type of subscript) - // - // Note: These portions can be read in a non-overlapping fashion and - // allow straightforward parsing of the hierarchy that WebGL encodes - // in the uniform names. - - function addUniform( container, uniformObject ) { - - container.seq.push( uniformObject ); - container.map[ uniformObject.id ] = uniformObject; - - } - - function parseUniform( activeInfo, addr, container ) { - - var path = activeInfo.name, - pathLength = path.length; - - // reset RegExp object, because of the early exit of a previous run - RePathPart.lastIndex = 0; - - while ( true ) { - - var match = RePathPart.exec( path ), - matchEnd = RePathPart.lastIndex, - - id = match[ 1 ], - idIsIndex = match[ 2 ] === ']', - subscript = match[ 3 ]; - - if ( idIsIndex ) { id = id | 0; } // convert to integer - - if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { - - // bare name or "pure" bottom-level array "[0]" suffix - - addUniform( container, subscript === undefined ? - new SingleUniform( id, activeInfo, addr ) : - new PureArrayUniform( id, activeInfo, addr ) ); - - break; - - } else { - - // step into inner node / create it in case it doesn't exist - - var map = container.map, next = map[ id ]; - - if ( next === undefined ) { - - next = new StructuredUniform( id ); - addUniform( container, next ); - - } - - container = next; - - } - - } - - } - - // Root Container - - function WebGLUniforms( gl, program ) { - - this.seq = []; - this.map = {}; - - var n = gl.getProgramParameter( program, 35718 ); - - for ( var i = 0; i < n; ++ i ) { - - var info = gl.getActiveUniform( program, i ), - addr = gl.getUniformLocation( program, info.name ); - - parseUniform( info, addr, this ); - - } - - } - - WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { - - var u = this.map[ name ]; - - if ( u !== undefined ) { u.setValue( gl, value, textures ); } - - }; - - WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { - - var v = object[ name ]; - - if ( v !== undefined ) { this.setValue( gl, name, v ); } - - }; - - - // Static interface - - WebGLUniforms.upload = function ( gl, seq, values, textures ) { - - for ( var i = 0, n = seq.length; i !== n; ++ i ) { - - var u = seq[ i ], - v = values[ u.id ]; - - if ( v.needsUpdate !== false ) { - - // note: always updating when .needsUpdate is undefined - u.setValue( gl, v.value, textures ); - - } - - } - - }; - - WebGLUniforms.seqWithValue = function ( seq, values ) { - - var r = []; - - for ( var i = 0, n = seq.length; i !== n; ++ i ) { - - var u = seq[ i ]; - if ( u.id in values ) { r.push( u ); } - - } - - return r; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLShader( gl, type, string ) { - - var shader = gl.createShader( type ); - - gl.shaderSource( shader, string ); - gl.compileShader( shader ); - - return shader; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var programIdCount = 0; - - function addLineNumbers( string ) { - - var lines = string.split( '\n' ); - - for ( var i = 0; i < lines.length; i ++ ) { - - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; - - } - - return lines.join( '\n' ); - - } - - function getEncodingComponents( encoding ) { - - switch ( encoding ) { - - case LinearEncoding: - return [ 'Linear', '( value )' ]; - case sRGBEncoding: - return [ 'sRGB', '( value )' ]; - case RGBEEncoding: - return [ 'RGBE', '( value )' ]; - case RGBM7Encoding: - return [ 'RGBM', '( value, 7.0 )' ]; - case RGBM16Encoding: - return [ 'RGBM', '( value, 16.0 )' ]; - case RGBDEncoding: - return [ 'RGBD', '( value, 256.0 )' ]; - case GammaEncoding: - return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; - case LogLuvEncoding: - return [ 'LogLuv', '( value )' ]; - default: - throw new Error( 'unsupported encoding: ' + encoding ); - - } - - } - - function getShaderErrors( gl, shader, type ) { - - var status = gl.getShaderParameter( shader, 35713 ); - var log = gl.getShaderInfoLog( shader ).trim(); - - if ( status && log === '' ) { return ''; } - - // --enable-privileged-webgl-extension - // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - - var source = gl.getShaderSource( shader ); - - return 'THREE.WebGLShader: gl.getShaderInfoLog() ' + type + '\n' + log + addLineNumbers( source ); - - } - - function getTexelDecodingFunction( functionName, encoding ) { - - var components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; - - } - - function getTexelEncodingFunction( functionName, encoding ) { - - var components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; - - } - - function getToneMappingFunction( functionName, toneMapping ) { - - var toneMappingName; - - switch ( toneMapping ) { - - case LinearToneMapping: - toneMappingName = 'Linear'; - break; - - case ReinhardToneMapping: - toneMappingName = 'Reinhard'; - break; - - case Uncharted2ToneMapping: - toneMappingName = 'Uncharted2'; - break; - - case CineonToneMapping: - toneMappingName = 'OptimizedCineon'; - break; - - case ACESFilmicToneMapping: - toneMappingName = 'ACESFilmic'; - break; - - default: - throw new Error( 'unsupported toneMapping: ' + toneMapping ); - - } - - return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; - - } - - function generateExtensions( parameters ) { - - var chunks = [ - ( parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', - ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', - ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', - ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' - ]; - - return chunks.filter( filterEmptyLine ).join( '\n' ); - - } - - function generateDefines( defines ) { - - var chunks = []; - - for ( var name in defines ) { - - var value = defines[ name ]; - - if ( value === false ) { continue; } - - chunks.push( '#define ' + name + ' ' + value ); - - } - - return chunks.join( '\n' ); - - } - - function fetchAttributeLocations( gl, program ) { - - var attributes = {}; - - var n = gl.getProgramParameter( program, 35721 ); - - for ( var i = 0; i < n; i ++ ) { - - var info = gl.getActiveAttrib( program, i ); - var name = info.name; - - // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); - - attributes[ name ] = gl.getAttribLocation( program, name ); - - } - - return attributes; - - } - - function filterEmptyLine( string ) { - - return string !== ''; - - } - - function replaceLightNums( string, parameters ) { - - return string - .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) - .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) - .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) - .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) - .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) - .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) - .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) - .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); - - } - - function replaceClippingPlaneNums( string, parameters ) { - - return string - .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) - .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); - - } - - // Resolve Includes - - var includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; - - function resolveIncludes( string ) { - - return string.replace( includePattern, includeReplacer ); - - } - - function includeReplacer( match, include ) { - - var string = ShaderChunk[ include ]; - - if ( string === undefined ) { - - throw new Error( 'Can not resolve #include <' + include + '>' ); - - } - - return resolveIncludes( string ); - - } - - // Unroll Loops - - var deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; - var unrollLoopPattern = /#pragma unroll_loop_start[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}[\s]+?#pragma unroll_loop_end/g; - - function unrollLoops( string ) { - - return string - .replace( unrollLoopPattern, loopReplacer ) - .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); - - } - - function deprecatedLoopReplacer( match, start, end, snippet ) { - - console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); - return loopReplacer( match, start, end, snippet ); - - } - - function loopReplacer( match, start, end, snippet ) { - - var string = ''; - - for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { - - string += snippet - .replace( /\[ i \]/g, '[ ' + i + ' ]' ) - .replace( /UNROLLED_LOOP_INDEX/g, i ); - - } - - return string; - - } - - // - - function generatePrecision( parameters ) { - - var precisionstring = "precision " + parameters.precision + " float;\nprecision " + parameters.precision + " int;"; - - if ( parameters.precision === "highp" ) { - - precisionstring += "\n#define HIGH_PRECISION"; - - } else if ( parameters.precision === "mediump" ) { - - precisionstring += "\n#define MEDIUM_PRECISION"; - - } else if ( parameters.precision === "lowp" ) { - - precisionstring += "\n#define LOW_PRECISION"; - - } - - return precisionstring; - - } - - function generateShadowMapTypeDefine( parameters ) { - - var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - - if ( parameters.shadowMapType === PCFShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - - } else if ( parameters.shadowMapType === VSMShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; - - } - - return shadowMapTypeDefine; - - } - - function generateEnvMapTypeDefine( parameters ) { - - var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; - - case CubeUVReflectionMapping: - case CubeUVRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; - - case EquirectangularReflectionMapping: - case EquirectangularRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; - break; - - case SphericalReflectionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; - break; - - } - - } - - return envMapTypeDefine; - - } - - function generateEnvMapModeDefine( parameters ) { - - var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeRefractionMapping: - case EquirectangularRefractionMapping: - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; - - } - - } - - return envMapModeDefine; - - } - - function generateEnvMapBlendingDefine( parameters ) { - - var envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; - - if ( parameters.envMap ) { - - switch ( parameters.combine ) { - - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; - - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; - - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; - - } - - } - - return envMapBlendingDefine; - - } - - function WebGLProgram( renderer, cacheKey, parameters ) { - - var gl = renderer.getContext(); - - var defines = parameters.defines; - - var vertexShader = parameters.vertexShader; - var fragmentShader = parameters.fragmentShader; - var shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); - var envMapTypeDefine = generateEnvMapTypeDefine( parameters ); - var envMapModeDefine = generateEnvMapModeDefine( parameters ); - var envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); - - - var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; - - var customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); - - var customDefines = generateDefines( defines ); - - var program = gl.createProgram(); - - var prefixVertex, prefixFragment; - - if ( parameters.isRawShaderMaterial ) { - - prefixVertex = [ - - customDefines - - ].filter( filterEmptyLine ).join( '\n' ); - - if ( prefixVertex.length > 0 ) { - - prefixVertex += '\n'; - - } - - prefixFragment = [ - - customExtensions, - customDefines - - ].filter( filterEmptyLine ).join( '\n' ); - - if ( prefixFragment.length > 0 ) { - - prefixFragment += '\n'; - - } - - } else { - - prefixVertex = [ - - generatePrecision( parameters ), - - '#define SHADER_NAME ' + parameters.shaderName, - - customDefines, - - parameters.instancing ? '#define USE_INSTANCING' : '', - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - '#define MAX_BONES ' + parameters.maxBones, - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', - - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', - - '#ifdef USE_INSTANCING', - - ' attribute mat4 instanceMatrix;', - - '#endif', - - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', - - '#ifdef USE_TANGENT', - - ' attribute vec4 tangent;', - - '#endif', - - '#ifdef USE_COLOR', - - ' attribute vec3 color;', - - '#endif', - - '#ifdef USE_MORPHTARGETS', - - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', - - ' #ifdef USE_MORPHNORMALS', - - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', - - ' #else', - - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', - - ' #endif', - - '#endif', - - '#ifdef USE_SKINNING', - - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', - - '#endif', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - prefixFragment = [ - - customExtensions, - - generatePrecision( parameters ), - - '#define SHADER_NAME ' + parameters.shaderName, - - customDefines, - - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer - - '#define GAMMA_FACTOR ' + gammaFactorDefine, - - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - - parameters.map ? '#define USE_MAP' : '', - parameters.matcap ? '#define USE_MATCAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - - parameters.sheen ? '#define USE_SHEEN' : '', - - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - - parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', - - parameters.flatShading ? '#define FLAT_SHADED' : '', - - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', - - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - - parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', - - parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', - - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - - ( ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ) ? '#define TEXTURE_LOD_EXT' : '', - - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', - - ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', - ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below - ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', - - parameters.dithering ? '#define DITHERING' : '', - - ( parameters.outputEncoding || parameters.mapEncoding || parameters.matcapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding || parameters.lightMapEncoding ) ? - ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below - parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.matcapEncoding ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', - parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.lightMapEncoding ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '', - parameters.outputEncoding ? getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ) : '', - - parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', - - '\n' - - ].filter( filterEmptyLine ).join( '\n' ); - - } - - vertexShader = resolveIncludes( vertexShader ); - vertexShader = replaceLightNums( vertexShader, parameters ); - vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); - - fragmentShader = resolveIncludes( fragmentShader ); - fragmentShader = replaceLightNums( fragmentShader, parameters ); - fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); - - vertexShader = unrollLoops( vertexShader ); - fragmentShader = unrollLoops( fragmentShader ); - - if ( parameters.isWebGL2 && ! parameters.isRawShaderMaterial ) { - - var isGLSL3ShaderMaterial = false; - - var versionRegex = /^\s*#version\s+300\s+es\s*\n/; - - if ( parameters.isShaderMaterial && - vertexShader.match( versionRegex ) !== null && - fragmentShader.match( versionRegex ) !== null ) { - - isGLSL3ShaderMaterial = true; - - vertexShader = vertexShader.replace( versionRegex, '' ); - fragmentShader = fragmentShader.replace( versionRegex, '' ); - - } - - // GLSL 3.0 conversion - - prefixVertex = [ - '#version 300 es\n', - '#define attribute in', - '#define varying out', - '#define texture2D texture' - ].join( '\n' ) + '\n' + prefixVertex; - - prefixFragment = [ - '#version 300 es\n', - '#define varying in', - isGLSL3ShaderMaterial ? '' : 'out highp vec4 pc_fragColor;', - isGLSL3ShaderMaterial ? '' : '#define gl_FragColor pc_fragColor', - '#define gl_FragDepthEXT gl_FragDepth', - '#define texture2D texture', - '#define textureCube texture', - '#define texture2DProj textureProj', - '#define texture2DLodEXT textureLod', - '#define texture2DProjLodEXT textureProjLod', - '#define textureCubeLodEXT textureLod', - '#define texture2DGradEXT textureGrad', - '#define texture2DProjGradEXT textureProjGrad', - '#define textureCubeGradEXT textureGrad' - ].join( '\n' ) + '\n' + prefixFragment; - - } - - var vertexGlsl = prefixVertex + vertexShader; - var fragmentGlsl = prefixFragment + fragmentShader; - - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); - - var glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); - var glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); - - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); - - // Force a particular attribute to index 0. - - if ( parameters.index0AttributeName !== undefined ) { - - gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); - - } else if ( parameters.morphTargets === true ) { - - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); - - } - - gl.linkProgram( program ); - - // check for link errors - if ( renderer.debug.checkShaderErrors ) { - - var programLog = gl.getProgramInfoLog( program ).trim(); - var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); - var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); - - var runnable = true; - var haveDiagnostics = true; - - if ( gl.getProgramParameter( program, 35714 ) === false ) { - - runnable = false; - - var vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); - var fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); - - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors ); - - } else if ( programLog !== '' ) { - - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); - - } else if ( vertexLog === '' || fragmentLog === '' ) { - - haveDiagnostics = false; - - } - - if ( haveDiagnostics ) { - - this.diagnostics = { - - runnable: runnable, - - programLog: programLog, - - vertexShader: { - - log: vertexLog, - prefix: prefixVertex - - }, - - fragmentShader: { - - log: fragmentLog, - prefix: prefixFragment - - } - - }; - - } - - } - - // Clean up - - // Crashes in iOS9 and iOS10. #18402 - // gl.detachShader( program, glVertexShader ); - // gl.detachShader( program, glFragmentShader ); - - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); - - // set up caching for uniform locations - - var cachedUniforms; - - this.getUniforms = function () { - - if ( cachedUniforms === undefined ) { - - cachedUniforms = new WebGLUniforms( gl, program ); - - } - - return cachedUniforms; - - }; - - // set up caching for attribute locations - - var cachedAttributes; - - this.getAttributes = function () { - - if ( cachedAttributes === undefined ) { - - cachedAttributes = fetchAttributeLocations( gl, program ); - - } - - return cachedAttributes; - - }; - - // free resource - - this.destroy = function () { - - gl.deleteProgram( program ); - this.program = undefined; - - }; - - // - - this.name = parameters.shaderName; - this.id = programIdCount ++; - this.cacheKey = cacheKey; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; - - return this; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLPrograms( renderer, extensions, capabilities ) { - - var programs = []; - - var isWebGL2 = capabilities.isWebGL2; - var logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; - var floatVertexTextures = capabilities.floatVertexTextures; - var precision = capabilities.precision; - var maxVertexUniforms = capabilities.maxVertexUniforms; - var vertexTextures = capabilities.vertexTextures; - - var shaderIDs = { - MeshDepthMaterial: 'depth', - MeshDistanceMaterial: 'distanceRGBA', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'toon', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical', - MeshMatcapMaterial: 'matcap', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points', - ShadowMaterial: 'shadow', - SpriteMaterial: 'sprite' - }; - - var parameterNames = [ - "precision", "isWebGL2", "supportsVertexTextures", "outputEncoding", "instancing", - "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", "envMapCubeUV", - "lightMap", "lightMapEncoding", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "tangentSpaceNormalMap", "clearcoatMap", "clearcoatRoughnessMap", "clearcoatNormalMap", "displacementMap", "specularMap", - "roughnessMap", "metalnessMap", "gradientMap", - "alphaMap", "combine", "vertexColors", "vertexTangents", "vertexUvs", "uvsVertexOnly", "fog", "useFog", "fogExp2", - "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", - "maxBones", "useVertexTexture", "morphTargets", "morphNormals", - "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", - "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", - "numDirLightShadows", "numPointLightShadows", "numSpotLightShadows", - "shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights', - "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering", - "sheen" - ]; - - function getShaderObject( material, shaderID ) { - - var shaderobject; - - if ( shaderID ) { - - var shader = ShaderLib[ shaderID ]; - - shaderobject = { - name: material.type, - uniforms: UniformsUtils.clone( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader - }; - - } else { - - shaderobject = { - name: material.type, - uniforms: material.uniforms, - vertexShader: material.vertexShader, - fragmentShader: material.fragmentShader - }; - - } - - return shaderobject; - - } - - function allocateBones( object ) { - - var skeleton = object.skeleton; - var bones = skeleton.bones; - - if ( floatVertexTextures ) { - - return 1024; - - } else { - - // default for when object is not specified - // ( for example when prebuilding shader to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) - - var nVertexUniforms = maxVertexUniforms; - var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - - var maxBones = Math.min( nVertexMatrices, bones.length ); - - if ( maxBones < bones.length ) { - - console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); - return 0; - - } - - return maxBones; - - } - - } - - function getTextureEncodingFromMap( map ) { - - var encoding; - - if ( ! map ) { - - encoding = LinearEncoding; - - } else if ( map.isTexture ) { - - encoding = map.encoding; - - } else if ( map.isWebGLRenderTarget ) { - - console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." ); - encoding = map.texture.encoding; - - } - - return encoding; - - } - - this.getParameters = function ( material, lights, shadows, scene, nClipPlanes, nClipIntersection, object ) { - - var fog = scene.fog; - var environment = material.isMeshStandardMaterial ? scene.environment : null; - - var envMap = material.envMap || environment; - - var shaderID = shaderIDs[ material.type ]; - - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) - - var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0; - - if ( material.precision !== null ) { - - precision = capabilities.getMaxPrecision( material.precision ); - - if ( precision !== material.precision ) { - - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); - - } - - } - - var shaderobject = getShaderObject( material, shaderID ); - material.onBeforeCompile( shaderobject, renderer ); - - var currentRenderTarget = renderer.getRenderTarget(); - - var parameters = { - - isWebGL2: isWebGL2, - - shaderID: shaderID, - shaderName: shaderobject.name, - - uniforms: shaderobject.uniforms, - vertexShader: shaderobject.vertexShader, - fragmentShader: shaderobject.fragmentShader, - defines: material.defines, - - isRawShaderMaterial: material.isRawShaderMaterial, - isShaderMaterial: material.isShaderMaterial, - - precision: precision, - - instancing: object.isInstancedMesh === true, - - supportsVertexTextures: vertexTextures, - outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding, - map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map ), - matcap: !! material.matcap, - matcapEncoding: getTextureEncodingFromMap( material.matcap ), - envMap: !! envMap, - envMapMode: envMap && envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( envMap ), - envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ), - lightMap: !! material.lightMap, - lightMapEncoding: getTextureEncodingFromMap( material.lightMap ), - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ), - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, - tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - clearcoatMap: !! material.clearcoatMap, - clearcoatRoughnessMap: !! material.clearcoatRoughnessMap, - clearcoatNormalMap: !! material.clearcoatNormalMap, - displacementMap: !! material.displacementMap, - roughnessMap: !! material.roughnessMap, - metalnessMap: !! material.metalnessMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, - - gradientMap: !! material.gradientMap, - - sheen: !! material.sheen, - - combine: material.combine, - - vertexTangents: ( material.normalMap && material.vertexTangents ), - vertexColors: material.vertexColors, - vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap, - uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap ) && !! material.displacementMap, - - fog: !! fog, - useFog: material.fog, - fogExp2: ( fog && fog.isFogExp2 ), - - flatShading: material.flatShading, - - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: logarithmicDepthBuffer, - - skinning: material.skinning && maxBones > 0, - maxBones: maxBones, - useVertexTexture: floatVertexTextures, - - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: renderer.maxMorphTargets, - maxMorphNormals: renderer.maxMorphNormals, - - numDirLights: lights.directional.length, - numPointLights: lights.point.length, - numSpotLights: lights.spot.length, - numRectAreaLights: lights.rectArea.length, - numHemiLights: lights.hemi.length, - - numDirLightShadows: lights.directionalShadowMap.length, - numPointLightShadows: lights.pointShadowMap.length, - numSpotLightShadows: lights.spotShadowMap.length, - - numClippingPlanes: nClipPlanes, - numClipIntersection: nClipIntersection, - - dithering: material.dithering, - - shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, - shadowMapType: renderer.shadowMap.type, - - toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, - physicallyCorrectLights: renderer.physicallyCorrectLights, - - premultipliedAlpha: material.premultipliedAlpha, - - alphaTest: material.alphaTest, - doubleSided: material.side === DoubleSide, - flipSided: material.side === BackSide, - - depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, - - index0AttributeName: material.index0AttributeName, - - extensionDerivatives: material.extensions && material.extensions.derivatives, - extensionFragDepth: material.extensions && material.extensions.fragDepth, - extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, - extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, - - rendererExtensionFragDepth: isWebGL2 || extensions.get( 'EXT_frag_depth' ) !== null, - rendererExtensionDrawBuffers: isWebGL2 || extensions.get( 'WEBGL_draw_buffers' ) !== null, - rendererExtensionShaderTextureLod: isWebGL2 || extensions.get( 'EXT_shader_texture_lod' ) !== null, - - onBeforeCompile: material.onBeforeCompile - - }; - - return parameters; - - }; - - this.getProgramCacheKey = function ( parameters ) { - - var array = []; - - if ( parameters.shaderID ) { - - array.push( parameters.shaderID ); - - } else { - - array.push( parameters.fragmentShader ); - array.push( parameters.vertexShader ); - - } - - if ( parameters.defines !== undefined ) { - - for ( var name in parameters.defines ) { - - array.push( name ); - array.push( parameters.defines[ name ] ); - - } - - } - - if ( parameters.isRawShaderMaterial === undefined ) { - - for ( var i = 0; i < parameterNames.length; i ++ ) { - - array.push( parameters[ parameterNames[ i ] ] ); - - } - - array.push( renderer.outputEncoding ); - array.push( renderer.gammaFactor ); - - } - - array.push( parameters.onBeforeCompile.toString() ); - - return array.join(); - - }; - - this.acquireProgram = function ( parameters, cacheKey ) { - - var program; - - // Check if code has been already compiled - for ( var p = 0, pl = programs.length; p < pl; p ++ ) { - - var preexistingProgram = programs[ p ]; - - if ( preexistingProgram.cacheKey === cacheKey ) { - - program = preexistingProgram; - ++ program.usedTimes; - - break; - - } - - } - - if ( program === undefined ) { - - program = new WebGLProgram( renderer, cacheKey, parameters ); - programs.push( program ); - - } - - return program; - - }; - - this.releaseProgram = function ( program ) { - - if ( -- program.usedTimes === 0 ) { - - // Remove from unordered set - var i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); - - // Free WebGL resources - program.destroy(); - - } - - }; - - // Exposed for resource monitoring & error feedback via renderer.info: - this.programs = programs; - - } - - /** - * @author fordacious / fordacious.github.io - */ - - function WebGLProperties() { - - var properties = new WeakMap(); - - function get( object ) { - - var map = properties.get( object ); - - if ( map === undefined ) { - - map = {}; - properties.set( object, map ); - - } - - return map; - - } - - function remove( object ) { - - properties.delete( object ); - - } - - function update( object, key, value ) { - - properties.get( object )[ key ] = value; - - } - - function dispose() { - - properties = new WeakMap(); - - } - - return { - get: get, - remove: remove, - update: update, - dispose: dispose - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function painterSortStable( a, b ) { - - if ( a.groupOrder !== b.groupOrder ) { - - return a.groupOrder - b.groupOrder; - - } else if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.program !== b.program ) { - - return a.program.id - b.program.id; - - } else if ( a.material.id !== b.material.id ) { - - return a.material.id - b.material.id; - - } else if ( a.z !== b.z ) { - - return a.z - b.z; - - } else { - - return a.id - b.id; - - } - - } - - function reversePainterSortStable( a, b ) { - - if ( a.groupOrder !== b.groupOrder ) { - - return a.groupOrder - b.groupOrder; - - } else if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.z !== b.z ) { - - return b.z - a.z; - - } else { - - return a.id - b.id; - - } - - } - - - function WebGLRenderList() { - - var renderItems = []; - var renderItemsIndex = 0; - - var opaque = []; - var transparent = []; - - var defaultProgram = { id: - 1 }; - - function init() { - - renderItemsIndex = 0; - - opaque.length = 0; - transparent.length = 0; - - } - - function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { - - var renderItem = renderItems[ renderItemsIndex ]; - - if ( renderItem === undefined ) { - - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - program: material.program || defaultProgram, - groupOrder: groupOrder, - renderOrder: object.renderOrder, - z: z, - group: group - }; - - renderItems[ renderItemsIndex ] = renderItem; - - } else { - - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.program = material.program || defaultProgram; - renderItem.groupOrder = groupOrder; - renderItem.renderOrder = object.renderOrder; - renderItem.z = z; - renderItem.group = group; - - } - - renderItemsIndex ++; - - return renderItem; - - } - - function push( object, geometry, material, groupOrder, z, group ) { - - var renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - - ( material.transparent === true ? transparent : opaque ).push( renderItem ); - - } - - function unshift( object, geometry, material, groupOrder, z, group ) { - - var renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - - ( material.transparent === true ? transparent : opaque ).unshift( renderItem ); - - } - - function sort( customOpaqueSort, customTransparentSort ) { - - if ( opaque.length > 1 ) { opaque.sort( customOpaqueSort || painterSortStable ); } - if ( transparent.length > 1 ) { transparent.sort( customTransparentSort || reversePainterSortStable ); } - - } - - function finish() { - - // Clear references from inactive renderItems in the list - - for ( var i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { - - var renderItem = renderItems[ i ]; - - if ( renderItem.id === null ) { break; } - - renderItem.id = null; - renderItem.object = null; - renderItem.geometry = null; - renderItem.material = null; - renderItem.program = null; - renderItem.group = null; - - } - - } - - return { - opaque: opaque, - transparent: transparent, - - init: init, - push: push, - unshift: unshift, - finish: finish, - - sort: sort - }; - - } - - function WebGLRenderLists() { - - var lists = new WeakMap(); - - function onSceneDispose( event ) { - - var scene = event.target; - - scene.removeEventListener( 'dispose', onSceneDispose ); - - lists.delete( scene ); - - } - - function get( scene, camera ) { - - var cameras = lists.get( scene ); - var list; - if ( cameras === undefined ) { - - list = new WebGLRenderList(); - lists.set( scene, new WeakMap() ); - lists.get( scene ).set( camera, list ); - - scene.addEventListener( 'dispose', onSceneDispose ); - - } else { - - list = cameras.get( camera ); - if ( list === undefined ) { - - list = new WebGLRenderList(); - cameras.set( camera, list ); - - } - - } - - return list; - - } - - function dispose() { - - lists = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function UniformsCache() { - - var lights = {}; - - return { - - get: function ( light ) { - - if ( lights[ light.id ] !== undefined ) { - - return lights[ light.id ]; - - } - - var uniforms; - - switch ( light.type ) { - - case 'DirectionalLight': - uniforms = { - direction: new Vector3(), - color: new Color() - }; - break; - - case 'SpotLight': - uniforms = { - position: new Vector3(), - direction: new Vector3(), - color: new Color(), - distance: 0, - coneCos: 0, - penumbraCos: 0, - decay: 0 - }; - break; - - case 'PointLight': - uniforms = { - position: new Vector3(), - color: new Color(), - distance: 0, - decay: 0 - }; - break; - - case 'HemisphereLight': - uniforms = { - direction: new Vector3(), - skyColor: new Color(), - groundColor: new Color() - }; - break; - - case 'RectAreaLight': - uniforms = { - color: new Color(), - position: new Vector3(), - halfWidth: new Vector3(), - halfHeight: new Vector3() - }; - break; - - } - - lights[ light.id ] = uniforms; - - return uniforms; - - } - - }; - - } - - function ShadowUniformsCache() { - - var lights = {}; - - return { - - get: function ( light ) { - - if ( lights[ light.id ] !== undefined ) { - - return lights[ light.id ]; - - } - - var uniforms; - - switch ( light.type ) { - - case 'DirectionalLight': - uniforms = { - shadowBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'SpotLight': - uniforms = { - shadowBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; - - case 'PointLight': - uniforms = { - shadowBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2(), - shadowCameraNear: 1, - shadowCameraFar: 1000 - }; - break; - - // TODO (abelnation): set RectAreaLight shadow uniforms - - } - - lights[ light.id ] = uniforms; - - return uniforms; - - } - - }; - - } - - - - var nextVersion = 0; - - function shadowCastingLightsFirst( lightA, lightB ) { - - return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); - - } - - function WebGLLights() { - - var cache = new UniformsCache(); - - var shadowCache = ShadowUniformsCache(); - - var state = { - - version: 0, - - hash: { - directionalLength: - 1, - pointLength: - 1, - spotLength: - 1, - rectAreaLength: - 1, - hemiLength: - 1, - - numDirectionalShadows: - 1, - numPointShadows: - 1, - numSpotShadows: - 1 - }, - - ambient: [ 0, 0, 0 ], - probe: [], - directional: [], - directionalShadow: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadow: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - point: [], - pointShadow: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [] - - }; - - for ( var i = 0; i < 9; i ++ ) { state.probe.push( new Vector3() ); } - - var vector3 = new Vector3(); - var matrix4 = new Matrix4(); - var matrix42 = new Matrix4(); - - function setup( lights, shadows, camera ) { - - var r = 0, g = 0, b = 0; - - for ( var i = 0; i < 9; i ++ ) { state.probe[ i ].set( 0, 0, 0 ); } - - var directionalLength = 0; - var pointLength = 0; - var spotLength = 0; - var rectAreaLength = 0; - var hemiLength = 0; - - var numDirectionalShadows = 0; - var numPointShadows = 0; - var numSpotShadows = 0; - - var viewMatrix = camera.matrixWorldInverse; - - lights.sort( shadowCastingLightsFirst ); - - for ( var i = 0, l = lights.length; i < l; i ++ ) { - - var light = lights[ i ]; - - var color = light.color; - var intensity = light.intensity; - var distance = light.distance; - - var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - - if ( light.isAmbientLight ) { - - r += color.r * intensity; - g += color.g * intensity; - b += color.b * intensity; - - } else if ( light.isLightProbe ) { - - for ( var j = 0; j < 9; j ++ ) { - - state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); - - } - - } else if ( light.isDirectionalLight ) { - - var uniforms = cache.get( light ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - if ( light.castShadow ) { - - var shadow = light.shadow; - - var shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - - state.directionalShadow[ directionalLength ] = shadowUniforms; - state.directionalShadowMap[ directionalLength ] = shadowMap; - state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - - numDirectionalShadows ++; - - } - - state.directional[ directionalLength ] = uniforms; - - directionalLength ++; - - } else if ( light.isSpotLight ) { - - var uniforms = cache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( color ).multiplyScalar( intensity ); - uniforms.distance = distance; - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); - - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = light.decay; - - if ( light.castShadow ) { - - var shadow = light.shadow; - - var shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - - state.spotShadow[ spotLength ] = shadowUniforms; - state.spotShadowMap[ spotLength ] = shadowMap; - state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; - - numSpotShadows ++; - - } - - state.spot[ spotLength ] = uniforms; - - spotLength ++; - - } else if ( light.isRectAreaLight ) { - - var uniforms = cache.get( light ); - - // (a) intensity is the total visible light emitted - //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); - - // (b) intensity is the brightness of the light - uniforms.color.copy( color ).multiplyScalar( intensity ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - // extract local rotation of light to derive width/height half vectors - matrix42.identity(); - matrix4.copy( light.matrixWorld ); - matrix4.premultiply( viewMatrix ); - matrix42.extractRotation( matrix4 ); - - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - - uniforms.halfWidth.applyMatrix4( matrix42 ); - uniforms.halfHeight.applyMatrix4( matrix42 ); - - // TODO (abelnation): RectAreaLight distance? - // uniforms.distance = distance; - - state.rectArea[ rectAreaLength ] = uniforms; - - rectAreaLength ++; - - } else if ( light.isPointLight ) { - - var uniforms = cache.get( light ); - - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); - - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.distance = light.distance; - uniforms.decay = light.decay; - - if ( light.castShadow ) { - - var shadow = light.shadow; - - var shadowUniforms = shadowCache.get( light ); - - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - shadowUniforms.shadowCameraNear = shadow.camera.near; - shadowUniforms.shadowCameraFar = shadow.camera.far; - - state.pointShadow[ pointLength ] = shadowUniforms; - state.pointShadowMap[ pointLength ] = shadowMap; - state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; - - numPointShadows ++; - - } - - state.point[ pointLength ] = uniforms; - - pointLength ++; - - } else if ( light.isHemisphereLight ) { - - var uniforms = cache.get( light ); - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); - - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); - - state.hemi[ hemiLength ] = uniforms; - - hemiLength ++; - - } - - } - - state.ambient[ 0 ] = r; - state.ambient[ 1 ] = g; - state.ambient[ 2 ] = b; - - var hash = state.hash; - - if ( hash.directionalLength !== directionalLength || - hash.pointLength !== pointLength || - hash.spotLength !== spotLength || - hash.rectAreaLength !== rectAreaLength || - hash.hemiLength !== hemiLength || - hash.numDirectionalShadows !== numDirectionalShadows || - hash.numPointShadows !== numPointShadows || - hash.numSpotShadows !== numSpotShadows ) { - - state.directional.length = directionalLength; - state.spot.length = spotLength; - state.rectArea.length = rectAreaLength; - state.point.length = pointLength; - state.hemi.length = hemiLength; - - state.directionalShadow.length = numDirectionalShadows; - state.directionalShadowMap.length = numDirectionalShadows; - state.pointShadow.length = numPointShadows; - state.pointShadowMap.length = numPointShadows; - state.spotShadow.length = numSpotShadows; - state.spotShadowMap.length = numSpotShadows; - state.directionalShadowMatrix.length = numDirectionalShadows; - state.pointShadowMatrix.length = numPointShadows; - state.spotShadowMatrix.length = numSpotShadows; - - hash.directionalLength = directionalLength; - hash.pointLength = pointLength; - hash.spotLength = spotLength; - hash.rectAreaLength = rectAreaLength; - hash.hemiLength = hemiLength; - - hash.numDirectionalShadows = numDirectionalShadows; - hash.numPointShadows = numPointShadows; - hash.numSpotShadows = numSpotShadows; - - state.version = nextVersion ++; - - } - - } - - return { - setup: setup, - state: state - }; - - } - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function WebGLRenderState() { - - var lights = new WebGLLights(); - - var lightsArray = []; - var shadowsArray = []; - - function init() { - - lightsArray.length = 0; - shadowsArray.length = 0; - - } - - function pushLight( light ) { - - lightsArray.push( light ); - - } - - function pushShadow( shadowLight ) { - - shadowsArray.push( shadowLight ); - - } - - function setupLights( camera ) { - - lights.setup( lightsArray, shadowsArray, camera ); - - } - - var state = { - lightsArray: lightsArray, - shadowsArray: shadowsArray, - - lights: lights - }; - - return { - init: init, - state: state, - setupLights: setupLights, - - pushLight: pushLight, - pushShadow: pushShadow - }; - - } - - function WebGLRenderStates() { - - var renderStates = new WeakMap(); - - function onSceneDispose( event ) { - - var scene = event.target; - - scene.removeEventListener( 'dispose', onSceneDispose ); - - renderStates.delete( scene ); - - } - - function get( scene, camera ) { - - var renderState; - - if ( renderStates.has( scene ) === false ) { - - renderState = new WebGLRenderState(); - renderStates.set( scene, new WeakMap() ); - renderStates.get( scene ).set( camera, renderState ); - - scene.addEventListener( 'dispose', onSceneDispose ); - - } else { - - if ( renderStates.get( scene ).has( camera ) === false ) { - - renderState = new WebGLRenderState(); - renderStates.get( scene ).set( camera, renderState ); - - } else { - - renderState = renderStates.get( scene ).get( camera ); - - } - - } - - return renderState; - - } - - function dispose() { - - renderStates = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author bhouston / https://clara.io - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * - * opacity: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ - - function MeshDepthMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshDepthMaterial'; - - this.depthPacking = BasicDepthPacking; - - this.skinning = false; - this.morphTargets = false; - - this.map = null; - - this.alphaMap = null; - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - - this.setValues( parameters ); - - } - - MeshDepthMaterial.prototype = Object.create( Material.prototype ); - MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; - - MeshDepthMaterial.prototype.isMeshDepthMaterial = true; - - MeshDepthMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.depthPacking = source.depthPacking; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - return this; - - }; - - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * - * referencePosition: , - * nearDistance: , - * farDistance: , - * - * skinning: , - * morphTargets: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: - * - * } - */ - - function MeshDistanceMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshDistanceMaterial'; - - this.referencePosition = new Vector3(); - this.nearDistance = 1; - this.farDistance = 1000; - - this.skinning = false; - this.morphTargets = false; - - this.map = null; - - this.alphaMap = null; - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.fog = false; - - this.setValues( parameters ); - - } - - MeshDistanceMaterial.prototype = Object.create( Material.prototype ); - MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial; - - MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; - - MeshDistanceMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.referencePosition.copy( source.referencePosition ); - this.nearDistance = source.nearDistance; - this.farDistance = source.farDistance; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - return this; - - }; - - var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n float mean = 0.0;\n float squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n for ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n #ifdef HORIZONAL_PASS\n vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n mean += distribution.x;\n squared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n #else\n float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n mean += depth;\n squared_mean += depth * depth;\n #endif\n }\n mean = mean * HALF_SAMPLE_RATE;\n squared_mean = squared_mean * HALF_SAMPLE_RATE;\n float std_dev = sqrt( squared_mean - mean * mean );\n gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; - - var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { - - var _frustum = new Frustum(), - - _shadowMapSize = new Vector2(), - _viewportSize = new Vector2(), - - _viewport = new Vector4(), - - _depthMaterials = [], - _distanceMaterials = [], - - _materialCache = {}; - - var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; - - var shadowMaterialVertical = new ShaderMaterial( { - - defines: { - SAMPLE_RATE: 2.0 / 8.0, - HALF_SAMPLE_RATE: 1.0 / 8.0 - }, - - uniforms: { - shadow_pass: { value: null }, - resolution: { value: new Vector2() }, - radius: { value: 4.0 } - }, - - vertexShader: vsm_vert, - - fragmentShader: vsm_frag - - } ); - - var shadowMaterialHorizonal = shadowMaterialVertical.clone(); - shadowMaterialHorizonal.defines.HORIZONAL_PASS = 1; - - var fullScreenTri = new BufferGeometry(); - fullScreenTri.setAttribute( - "position", - new BufferAttribute( - new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), - 3 - ) - ); - - var fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); - - var scope = this; - - this.enabled = false; - - this.autoUpdate = true; - this.needsUpdate = false; - - this.type = PCFShadowMap; - - this.render = function ( lights, scene, camera ) { - - if ( scope.enabled === false ) { return; } - if ( scope.autoUpdate === false && scope.needsUpdate === false ) { return; } - - if ( lights.length === 0 ) { return; } - - var currentRenderTarget = _renderer.getRenderTarget(); - var activeCubeFace = _renderer.getActiveCubeFace(); - var activeMipmapLevel = _renderer.getActiveMipmapLevel(); - - var _state = _renderer.state; - - // Set GL state for depth map. - _state.setBlending( NoBlending ); - _state.buffers.color.setClear( 1, 1, 1, 1 ); - _state.buffers.depth.setTest( true ); - _state.setScissorTest( false ); - - // render depth map - - for ( var i = 0, il = lights.length; i < il; i ++ ) { - - var light = lights[ i ]; - var shadow = light.shadow; - - if ( shadow === undefined ) { - - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); - continue; - - } - - _shadowMapSize.copy( shadow.mapSize ); - - var shadowFrameExtents = shadow.getFrameExtents(); - - _shadowMapSize.multiply( shadowFrameExtents ); - - _viewportSize.copy( shadow.mapSize ); - - if ( _shadowMapSize.x > maxTextureSize || _shadowMapSize.y > maxTextureSize ) { - - if ( _shadowMapSize.x > maxTextureSize ) { - - _viewportSize.x = Math.floor( maxTextureSize / shadowFrameExtents.x ); - _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; - shadow.mapSize.x = _viewportSize.x; - - } - - if ( _shadowMapSize.y > maxTextureSize ) { - - _viewportSize.y = Math.floor( maxTextureSize / shadowFrameExtents.y ); - _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; - shadow.mapSize.y = _viewportSize.y; - - } - - } - - if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - - var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; - - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + ".shadowMap"; - - shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - - shadow.camera.updateProjectionMatrix(); - - } - - if ( shadow.map === null ) { - - var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; - - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + ".shadowMap"; - - shadow.camera.updateProjectionMatrix(); - - } - - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - - var viewportCount = shadow.getViewportCount(); - - for ( var vp = 0; vp < viewportCount; vp ++ ) { - - var viewport = shadow.getViewport( vp ); - - _viewport.set( - _viewportSize.x * viewport.x, - _viewportSize.y * viewport.y, - _viewportSize.x * viewport.z, - _viewportSize.y * viewport.w - ); - - _state.viewport( _viewport ); - - shadow.updateMatrices( light, vp ); - - _frustum = shadow.getFrustum(); - - renderObject( scene, camera, shadow.camera, light, this.type ); - - } - - // do blur pass for VSM - - if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - - VSMPass( shadow, camera ); - - } - - } - - scope.needsUpdate = false; - - _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); - - }; - - function VSMPass( shadow, camera ) { - - var geometry = _objects.update( fullScreenMesh ); - - // vertical pass - - shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; - shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; - shadowMaterialVertical.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.mapPass ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); - - // horizonal pass - - shadowMaterialHorizonal.uniforms.shadow_pass.value = shadow.mapPass.texture; - shadowMaterialHorizonal.uniforms.resolution.value = shadow.mapSize; - shadowMaterialHorizonal.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizonal, fullScreenMesh, null ); - - } - - function getDepthMaterialVariant( useMorphing, useSkinning, useInstancing ) { - - var index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; - - var material = _depthMaterials[ index ]; - - if ( material === undefined ) { - - material = new MeshDepthMaterial( { - - depthPacking: RGBADepthPacking, - - morphTargets: useMorphing, - skinning: useSkinning - - } ); - - _depthMaterials[ index ] = material; - - } - - return material; - - } - - function getDistanceMaterialVariant( useMorphing, useSkinning, useInstancing ) { - - var index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; - - var material = _distanceMaterials[ index ]; - - if ( material === undefined ) { - - material = new MeshDistanceMaterial( { - - morphTargets: useMorphing, - skinning: useSkinning - - } ); - - _distanceMaterials[ index ] = material; - - } - - return material; - - } - - function getDepthMaterial( object, geometry, material, light, shadowCameraNear, shadowCameraFar, type ) { - - var result = null; - - var getMaterialVariant = getDepthMaterialVariant; - var customMaterial = object.customDepthMaterial; - - if ( light.isPointLight === true ) { - - getMaterialVariant = getDistanceMaterialVariant; - customMaterial = object.customDistanceMaterial; - - } - - if ( customMaterial === undefined ) { - - var useMorphing = false; - - if ( material.morphTargets === true ) { - - useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; - - } - - var useSkinning = false; - - if ( object.isSkinnedMesh === true ) { - - if ( material.skinning === true ) { - - useSkinning = true; - - } else { - - console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object ); - - } - - } - - var useInstancing = object.isInstancedMesh === true; - - result = getMaterialVariant( useMorphing, useSkinning, useInstancing ); - - } else { - - result = customMaterial; - - } - - if ( _renderer.localClippingEnabled && - material.clipShadows === true && - material.clippingPlanes.length !== 0 ) { - - // in this case we need a unique material instance reflecting the - // appropriate state - - var keyA = result.uuid, keyB = material.uuid; - - var materialsForVariant = _materialCache[ keyA ]; - - if ( materialsForVariant === undefined ) { - - materialsForVariant = {}; - _materialCache[ keyA ] = materialsForVariant; - - } - - var cachedMaterial = materialsForVariant[ keyB ]; - - if ( cachedMaterial === undefined ) { - - cachedMaterial = result.clone(); - materialsForVariant[ keyB ] = cachedMaterial; - - } - - result = cachedMaterial; - - } - - result.visible = material.visible; - result.wireframe = material.wireframe; - - if ( type === VSMShadowMap ) { - - result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; - - } else { - - result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; - - } - - result.clipShadows = material.clipShadows; - result.clippingPlanes = material.clippingPlanes; - result.clipIntersection = material.clipIntersection; - - result.wireframeLinewidth = material.wireframeLinewidth; - result.linewidth = material.linewidth; - - if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { - - result.referencePosition.setFromMatrixPosition( light.matrixWorld ); - result.nearDistance = shadowCameraNear; - result.farDistance = shadowCameraFar; - - } - - return result; - - } - - function renderObject( object, camera, shadowCamera, light, type ) { - - if ( object.visible === false ) { return; } - - var visible = object.layers.test( camera.layers ); - - if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - - if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { - - object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - - var geometry = _objects.update( object ); - var material = object.material; - - if ( Array.isArray( material ) ) { - - var groups = geometry.groups; - - for ( var k = 0, kl = groups.length; k < kl; k ++ ) { - - var group = groups[ k ]; - var groupMaterial = material[ group.materialIndex ]; - - if ( groupMaterial && groupMaterial.visible ) { - - var depthMaterial = getDepthMaterial( object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); - - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - - } - - } - - } else if ( material.visible ) { - - var depthMaterial = getDepthMaterial( object, geometry, material, light, shadowCamera.near, shadowCamera.far, type ); - - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); - - } - - } - - } - - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - renderObject( children[ i ], camera, shadowCamera, light, type ); - - } - - } - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLState( gl, extensions, capabilities ) { - - var isWebGL2 = capabilities.isWebGL2; - - function ColorBuffer() { - - var locked = false; - - var color = new Vector4(); - var currentColorMask = null; - var currentColorClear = new Vector4( 0, 0, 0, 0 ); - - return { - - setMask: function ( colorMask ) { - - if ( currentColorMask !== colorMask && ! locked ) { - - gl.colorMask( colorMask, colorMask, colorMask, colorMask ); - currentColorMask = colorMask; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( r, g, b, a, premultipliedAlpha ) { - - if ( premultipliedAlpha === true ) { - - r *= a; g *= a; b *= a; - - } - - color.set( r, g, b, a ); - - if ( currentColorClear.equals( color ) === false ) { - - gl.clearColor( r, g, b, a ); - currentColorClear.copy( color ); - - } - - }, - - reset: function () { - - locked = false; - - currentColorMask = null; - currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state - - } - - }; - - } - - function DepthBuffer() { - - var locked = false; - - var currentDepthMask = null; - var currentDepthFunc = null; - var currentDepthClear = null; - - return { - - setTest: function ( depthTest ) { - - if ( depthTest ) { - - enable( 2929 ); - - } else { - - disable( 2929 ); - - } - - }, - - setMask: function ( depthMask ) { - - if ( currentDepthMask !== depthMask && ! locked ) { - - gl.depthMask( depthMask ); - currentDepthMask = depthMask; - - } - - }, - - setFunc: function ( depthFunc ) { - - if ( currentDepthFunc !== depthFunc ) { - - if ( depthFunc ) { - - switch ( depthFunc ) { - - case NeverDepth: - - gl.depthFunc( 512 ); - break; - - case AlwaysDepth: - - gl.depthFunc( 519 ); - break; - - case LessDepth: - - gl.depthFunc( 513 ); - break; - - case LessEqualDepth: - - gl.depthFunc( 515 ); - break; - - case EqualDepth: - - gl.depthFunc( 514 ); - break; - - case GreaterEqualDepth: - - gl.depthFunc( 518 ); - break; - - case GreaterDepth: - - gl.depthFunc( 516 ); - break; - - case NotEqualDepth: - - gl.depthFunc( 517 ); - break; - - default: - - gl.depthFunc( 515 ); - - } - - } else { - - gl.depthFunc( 515 ); - - } - - currentDepthFunc = depthFunc; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( depth ) { - - if ( currentDepthClear !== depth ) { - - gl.clearDepth( depth ); - currentDepthClear = depth; - - } - - }, - - reset: function () { - - locked = false; - - currentDepthMask = null; - currentDepthFunc = null; - currentDepthClear = null; - - } - - }; - - } - - function StencilBuffer() { - - var locked = false; - - var currentStencilMask = null; - var currentStencilFunc = null; - var currentStencilRef = null; - var currentStencilFuncMask = null; - var currentStencilFail = null; - var currentStencilZFail = null; - var currentStencilZPass = null; - var currentStencilClear = null; - - return { - - setTest: function ( stencilTest ) { - - if ( ! locked ) { - - if ( stencilTest ) { - - enable( 2960 ); - - } else { - - disable( 2960 ); - - } - - } - - }, - - setMask: function ( stencilMask ) { - - if ( currentStencilMask !== stencilMask && ! locked ) { - - gl.stencilMask( stencilMask ); - currentStencilMask = stencilMask; - - } - - }, - - setFunc: function ( stencilFunc, stencilRef, stencilMask ) { - - if ( currentStencilFunc !== stencilFunc || - currentStencilRef !== stencilRef || - currentStencilFuncMask !== stencilMask ) { - - gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - - currentStencilFunc = stencilFunc; - currentStencilRef = stencilRef; - currentStencilFuncMask = stencilMask; - - } - - }, - - setOp: function ( stencilFail, stencilZFail, stencilZPass ) { - - if ( currentStencilFail !== stencilFail || - currentStencilZFail !== stencilZFail || - currentStencilZPass !== stencilZPass ) { - - gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - - currentStencilFail = stencilFail; - currentStencilZFail = stencilZFail; - currentStencilZPass = stencilZPass; - - } - - }, - - setLocked: function ( lock ) { - - locked = lock; - - }, - - setClear: function ( stencil ) { - - if ( currentStencilClear !== stencil ) { - - gl.clearStencil( stencil ); - currentStencilClear = stencil; - - } - - }, - - reset: function () { - - locked = false; - - currentStencilMask = null; - currentStencilFunc = null; - currentStencilRef = null; - currentStencilFuncMask = null; - currentStencilFail = null; - currentStencilZFail = null; - currentStencilZPass = null; - currentStencilClear = null; - - } - - }; - - } - - // - - var colorBuffer = new ColorBuffer(); - var depthBuffer = new DepthBuffer(); - var stencilBuffer = new StencilBuffer(); - - var maxVertexAttributes = gl.getParameter( 34921 ); - var newAttributes = new Uint8Array( maxVertexAttributes ); - var enabledAttributes = new Uint8Array( maxVertexAttributes ); - var attributeDivisors = new Uint8Array( maxVertexAttributes ); - - var enabledCapabilities = {}; - - var currentProgram = null; - - var currentBlendingEnabled = null; - var currentBlending = null; - var currentBlendEquation = null; - var currentBlendSrc = null; - var currentBlendDst = null; - var currentBlendEquationAlpha = null; - var currentBlendSrcAlpha = null; - var currentBlendDstAlpha = null; - var currentPremultipledAlpha = false; - - var currentFlipSided = null; - var currentCullFace = null; - - var currentLineWidth = null; - - var currentPolygonOffsetFactor = null; - var currentPolygonOffsetUnits = null; - - var maxTextures = gl.getParameter( 35661 ); - - var lineWidthAvailable = false; - var version = 0; - var glVersion = gl.getParameter( 7938 ); - - if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { - - version = parseFloat( /^WebGL\ ([0-9])/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 1.0 ); - - } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { - - version = parseFloat( /^OpenGL\ ES\ ([0-9])/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 2.0 ); - - } - - var currentTextureSlot = null; - var currentBoundTextures = {}; - - var currentScissor = new Vector4(); - var currentViewport = new Vector4(); - - function createTexture( type, target, count ) { - - var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. - var texture = gl.createTexture(); - - gl.bindTexture( type, texture ); - gl.texParameteri( type, 10241, 9728 ); - gl.texParameteri( type, 10240, 9728 ); - - for ( var i = 0; i < count; i ++ ) { - - gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); - - } - - return texture; - - } - - var emptyTextures = {}; - emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); - emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); - - // init - - colorBuffer.setClear( 0, 0, 0, 1 ); - depthBuffer.setClear( 1 ); - stencilBuffer.setClear( 0 ); - - enable( 2929 ); - depthBuffer.setFunc( LessEqualDepth ); - - setFlipSided( false ); - setCullFace( CullFaceBack ); - enable( 2884 ); - - setBlending( NoBlending ); - - // - - function initAttributes() { - - for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { - - newAttributes[ i ] = 0; - - } - - } - - function enableAttribute( attribute ) { - - enableAttributeAndDivisor( attribute, 0 ); - - } - - function enableAttributeAndDivisor( attribute, meshPerAttribute ) { - - newAttributes[ attribute ] = 1; - - if ( enabledAttributes[ attribute ] === 0 ) { - - gl.enableVertexAttribArray( attribute ); - enabledAttributes[ attribute ] = 1; - - } - - if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { - - var extension = isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' ); - - extension[ isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute ); - attributeDivisors[ attribute ] = meshPerAttribute; - - } - - } - - function disableUnusedAttributes() { - - for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { - - if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { - - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; - - } - - } - - } - - function vertexAttribPointer( index, size, type, normalized, stride, offset ) { - - if ( isWebGL2 === true && ( type === 5124 || type === 5125 ) ) { - - gl.vertexAttribIPointer( index, size, type, normalized, stride, offset ); - - } else { - - gl.vertexAttribPointer( index, size, type, normalized, stride, offset ); - - } - - } - - function enable( id ) { - - if ( enabledCapabilities[ id ] !== true ) { - - gl.enable( id ); - enabledCapabilities[ id ] = true; - - } - - } - - function disable( id ) { - - if ( enabledCapabilities[ id ] !== false ) { - - gl.disable( id ); - enabledCapabilities[ id ] = false; - - } - - } - - function useProgram( program ) { - - if ( currentProgram !== program ) { - - gl.useProgram( program ); - - currentProgram = program; - - return true; - - } - - return false; - - } - - var equationToGL = {}; - equationToGL[ AddEquation ] = 32774; - equationToGL[ SubtractEquation ] = 32778; - equationToGL[ ReverseSubtractEquation ] = 32779; - - if ( isWebGL2 ) { - - equationToGL[ MinEquation ] = 32775; - equationToGL[ MaxEquation ] = 32776; - - } else { - - var extension = extensions.get( 'EXT_blend_minmax' ); - - if ( extension !== null ) { - - equationToGL[ MinEquation ] = extension.MIN_EXT; - equationToGL[ MaxEquation ] = extension.MAX_EXT; - - } - - } - - var factorToGL = {}; - factorToGL[ ZeroFactor ] = 0; - factorToGL[ OneFactor ] = 1; - factorToGL[ SrcColorFactor ] = 768; - factorToGL[ SrcAlphaFactor ] = 770; - factorToGL[ SrcAlphaSaturateFactor ] = 776; - factorToGL[ DstColorFactor ] = 774; - factorToGL[ DstAlphaFactor ] = 772; - factorToGL[ OneMinusSrcColorFactor ] = 769; - factorToGL[ OneMinusSrcAlphaFactor ] = 771; - factorToGL[ OneMinusDstColorFactor ] = 775; - factorToGL[ OneMinusDstAlphaFactor ] = 773; - - function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - - if ( blending === NoBlending ) { - - if ( currentBlendingEnabled ) { - - disable( 3042 ); - currentBlendingEnabled = false; - - } - - return; - - } - - if ( ! currentBlendingEnabled ) { - - enable( 3042 ); - currentBlendingEnabled = true; - - } - - if ( blending !== CustomBlending ) { - - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - - if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { - - gl.blendEquation( 32774 ); - - currentBlendEquation = AddEquation; - currentBlendEquationAlpha = AddEquation; - - } - - if ( premultipliedAlpha ) { - - switch ( blending ) { - - case NormalBlending: - gl.blendFuncSeparate( 1, 771, 1, 771 ); - break; - - case AdditiveBlending: - gl.blendFunc( 1, 1 ); - break; - - case SubtractiveBlending: - gl.blendFuncSeparate( 0, 0, 769, 771 ); - break; - - case MultiplyBlending: - gl.blendFuncSeparate( 0, 768, 0, 770 ); - break; - - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; - - } - - } else { - - switch ( blending ) { - - case NormalBlending: - gl.blendFuncSeparate( 770, 771, 1, 771 ); - break; - - case AdditiveBlending: - gl.blendFunc( 770, 1 ); - break; - - case SubtractiveBlending: - gl.blendFunc( 0, 769 ); - break; - - case MultiplyBlending: - gl.blendFunc( 0, 768 ); - break; - - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; - - } - - } - - currentBlendSrc = null; - currentBlendDst = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - - currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; - - } - - return; - - } - - // custom blending - - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; - - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - - gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); - - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; - - } - - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - - gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); - - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; - - } - - currentBlending = blending; - currentPremultipledAlpha = null; - - } - - function setMaterial( material, frontFaceCW ) { - - material.side === DoubleSide - ? disable( 2884 ) - : enable( 2884 ); - - var flipSided = ( material.side === BackSide ); - if ( frontFaceCW ) { flipSided = ! flipSided; } - - setFlipSided( flipSided ); - - ( material.blending === NormalBlending && material.transparent === false ) - ? setBlending( NoBlending ) - : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - - depthBuffer.setFunc( material.depthFunc ); - depthBuffer.setTest( material.depthTest ); - depthBuffer.setMask( material.depthWrite ); - colorBuffer.setMask( material.colorWrite ); - - var stencilWrite = material.stencilWrite; - stencilBuffer.setTest( stencilWrite ); - if ( stencilWrite ) { - - stencilBuffer.setMask( material.stencilWriteMask ); - stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); - stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); - - } - - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - - } - - // - - function setFlipSided( flipSided ) { - - if ( currentFlipSided !== flipSided ) { - - if ( flipSided ) { - - gl.frontFace( 2304 ); - - } else { - - gl.frontFace( 2305 ); - - } - - currentFlipSided = flipSided; - - } - - } - - function setCullFace( cullFace ) { - - if ( cullFace !== CullFaceNone ) { - - enable( 2884 ); - - if ( cullFace !== currentCullFace ) { - - if ( cullFace === CullFaceBack ) { - - gl.cullFace( 1029 ); - - } else if ( cullFace === CullFaceFront ) { - - gl.cullFace( 1028 ); - - } else { - - gl.cullFace( 1032 ); - - } - - } - - } else { - - disable( 2884 ); - - } - - currentCullFace = cullFace; - - } - - function setLineWidth( width ) { - - if ( width !== currentLineWidth ) { - - if ( lineWidthAvailable ) { gl.lineWidth( width ); } - - currentLineWidth = width; - - } - - } - - function setPolygonOffset( polygonOffset, factor, units ) { - - if ( polygonOffset ) { - - enable( 32823 ); - - if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - - gl.polygonOffset( factor, units ); - - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; - - } - - } else { - - disable( 32823 ); - - } - - } - - function setScissorTest( scissorTest ) { - - if ( scissorTest ) { - - enable( 3089 ); - - } else { - - disable( 3089 ); - - } - - } - - // texture - - function activeTexture( webglSlot ) { - - if ( webglSlot === undefined ) { webglSlot = 33984 + maxTextures - 1; } - - if ( currentTextureSlot !== webglSlot ) { - - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; - - } - - } - - function bindTexture( webglType, webglTexture ) { - - if ( currentTextureSlot === null ) { - - activeTexture(); - - } - - var boundTexture = currentBoundTextures[ currentTextureSlot ]; - - if ( boundTexture === undefined ) { - - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; - - } - - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - - gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); - - boundTexture.type = webglType; - boundTexture.texture = webglTexture; - - } - - } - - function unbindTexture() { - - var boundTexture = currentBoundTextures[ currentTextureSlot ]; - - if ( boundTexture !== undefined && boundTexture.type !== undefined ) { - - gl.bindTexture( boundTexture.type, null ); - - boundTexture.type = undefined; - boundTexture.texture = undefined; - - } - - } - - function compressedTexImage2D() { - - try { - - gl.compressedTexImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - function texImage2D() { - - try { - - gl.texImage2D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - function texImage3D() { - - try { - - gl.texImage3D.apply( gl, arguments ); - - } catch ( error ) { - - console.error( 'THREE.WebGLState:', error ); - - } - - } - - // - - function scissor( scissor ) { - - if ( currentScissor.equals( scissor ) === false ) { - - gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); - currentScissor.copy( scissor ); - - } - - } - - function viewport( viewport ) { - - if ( currentViewport.equals( viewport ) === false ) { - - gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); - currentViewport.copy( viewport ); - - } - - } - - // - - function reset() { - - for ( var i = 0; i < enabledAttributes.length; i ++ ) { - - if ( enabledAttributes[ i ] === 1 ) { - - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; - - } - - } - - enabledCapabilities = {}; - - currentTextureSlot = null; - currentBoundTextures = {}; - - currentProgram = null; - - currentBlending = null; - - currentFlipSided = null; - currentCullFace = null; - - colorBuffer.reset(); - depthBuffer.reset(); - stencilBuffer.reset(); - - } - - return { - - buffers: { - color: colorBuffer, - depth: depthBuffer, - stencil: stencilBuffer - }, - - initAttributes: initAttributes, - enableAttribute: enableAttribute, - enableAttributeAndDivisor: enableAttributeAndDivisor, - disableUnusedAttributes: disableUnusedAttributes, - vertexAttribPointer: vertexAttribPointer, - enable: enable, - disable: disable, - - useProgram: useProgram, - - setBlending: setBlending, - setMaterial: setMaterial, - - setFlipSided: setFlipSided, - setCullFace: setCullFace, - - setLineWidth: setLineWidth, - setPolygonOffset: setPolygonOffset, - - setScissorTest: setScissorTest, - - activeTexture: activeTexture, - bindTexture: bindTexture, - unbindTexture: unbindTexture, - compressedTexImage2D: compressedTexImage2D, - texImage2D: texImage2D, - texImage3D: texImage3D, - - scissor: scissor, - viewport: viewport, - - reset: reset - - }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { - - var isWebGL2 = capabilities.isWebGL2; - var maxTextures = capabilities.maxTextures; - var maxCubemapSize = capabilities.maxCubemapSize; - var maxTextureSize = capabilities.maxTextureSize; - var maxSamples = capabilities.maxSamples; - - var _videoTextures = new WeakMap(); - var _canvas; - - // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, - // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! - // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). - - var useOffscreenCanvas = false; - - try { - - useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' - && ( new OffscreenCanvas( 1, 1 ).getContext( "2d" ) ) !== null; - - } catch ( err ) { - - // Ignore any errors - - } - - function createCanvas( width, height ) { - - // Use OffscreenCanvas when available. Specially needed in web workers - - return useOffscreenCanvas ? - new OffscreenCanvas( width, height ) : - document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - - } - - function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { - - var scale = 1; - - // handle case if texture exceeds max size - - if ( image.width > maxSize || image.height > maxSize ) { - - scale = maxSize / Math.max( image.width, image.height ); - - } - - // only perform resize if necessary - - if ( scale < 1 || needsPowerOfTwo === true ) { - - // only perform resize for certain image types - - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - - var floor = needsPowerOfTwo ? MathUtils.floorPowerOfTwo : Math.floor; - - var width = floor( scale * image.width ); - var height = floor( scale * image.height ); - - if ( _canvas === undefined ) { _canvas = createCanvas( width, height ); } - - // cube textures can't reuse the same canvas - - var canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; - - canvas.width = width; - canvas.height = height; - - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); - - console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); - - return canvas; - - } else { - - if ( 'data' in image ) { - - console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); - - } - - return image; - - } - - } - - return image; - - } - - function isPowerOfTwo( image ) { - - return MathUtils.isPowerOfTwo( image.width ) && MathUtils.isPowerOfTwo( image.height ); - - } - - function textureNeedsPowerOfTwo( texture ) { - - if ( isWebGL2 ) { return false; } - - return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || - ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); - - } - - function textureNeedsGenerateMipmaps( texture, supportsMips ) { - - return texture.generateMipmaps && supportsMips && - texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; - - } - - function generateMipmap( target, texture, width, height ) { - - _gl.generateMipmap( target ); - - var textureProperties = properties.get( texture ); - - // Note: Math.log( x ) * Math.LOG2E used instead of Math.log2( x ) which is not supported by IE11 - textureProperties.__maxMipLevel = Math.log( Math.max( width, height ) ) * Math.LOG2E; - - } - - function getInternalFormat( internalFormatName, glFormat, glType ) { - - if ( isWebGL2 === false ) { return glFormat; } - - if ( internalFormatName !== null ) { - - if ( _gl[ internalFormatName ] !== undefined ) { return _gl[ internalFormatName ]; } - - console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); - - } - - var internalFormat = glFormat; - - if ( glFormat === 6403 ) { - - if ( glType === 5126 ) { internalFormat = 33326; } - if ( glType === 5131 ) { internalFormat = 33325; } - if ( glType === 5121 ) { internalFormat = 33321; } - - } - - if ( glFormat === 6407 ) { - - if ( glType === 5126 ) { internalFormat = 34837; } - if ( glType === 5131 ) { internalFormat = 34843; } - if ( glType === 5121 ) { internalFormat = 32849; } - - } - - if ( glFormat === 6408 ) { - - if ( glType === 5126 ) { internalFormat = 34836; } - if ( glType === 5131 ) { internalFormat = 34842; } - if ( glType === 5121 ) { internalFormat = 32856; } - - } - - if ( internalFormat === 33325 || internalFormat === 33326 || - internalFormat === 34842 || internalFormat === 34836 ) { - - extensions.get( 'EXT_color_buffer_float' ); - - } - - return internalFormat; - - } - - // Fallback filters for non-power-of-2 textures - - function filterFallback( f ) { - - if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { - - return 9728; - - } - - return 9729; - - } - - // - - function onTextureDispose( event ) { - - var texture = event.target; - - texture.removeEventListener( 'dispose', onTextureDispose ); - - deallocateTexture( texture ); - - if ( texture.isVideoTexture ) { - - _videoTextures.delete( texture ); - - } - - info.memory.textures --; - - } - - function onRenderTargetDispose( event ) { - - var renderTarget = event.target; - - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - - deallocateRenderTarget( renderTarget ); - - info.memory.textures --; - - } - - // - - function deallocateTexture( texture ) { - - var textureProperties = properties.get( texture ); - - if ( textureProperties.__webglInit === undefined ) { return; } - - _gl.deleteTexture( textureProperties.__webglTexture ); - - properties.remove( texture ); - - } - - function deallocateRenderTarget( renderTarget ) { - - var renderTargetProperties = properties.get( renderTarget ); - var textureProperties = properties.get( renderTarget.texture ); - - if ( ! renderTarget ) { return; } - - if ( textureProperties.__webglTexture !== undefined ) { - - _gl.deleteTexture( textureProperties.__webglTexture ); - - } - - if ( renderTarget.depthTexture ) { - - renderTarget.depthTexture.dispose(); - - } - - if ( renderTarget.isWebGLCubeRenderTarget ) { - - for ( var i = 0; i < 6; i ++ ) { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - if ( renderTargetProperties.__webglDepthbuffer ) { _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } - - } - - } else { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - if ( renderTargetProperties.__webglDepthbuffer ) { _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); } - if ( renderTargetProperties.__webglMultisampledFramebuffer ) { _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); } - if ( renderTargetProperties.__webglColorRenderbuffer ) { _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); } - if ( renderTargetProperties.__webglDepthRenderbuffer ) { _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); } - - } - - properties.remove( renderTarget.texture ); - properties.remove( renderTarget ); - - } - - // - - var textureUnits = 0; - - function resetTextureUnits() { - - textureUnits = 0; - - } - - function allocateTextureUnit() { - - var textureUnit = textureUnits; - - if ( textureUnit >= maxTextures ) { - - console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); - - } - - textureUnits += 1; - - return textureUnit; - - } - - // - - function setTexture2D( texture, slot ) { - - var textureProperties = properties.get( texture ); - - if ( texture.isVideoTexture ) { updateVideoTexture( texture ); } - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - var image = texture.image; - - if ( image === undefined ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' ); - - } else if ( image.complete === false ) { - - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); - - } else { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 3553, textureProperties.__webglTexture ); - - } - - function setTexture2DArray( texture, slot ) { - - var textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 35866, textureProperties.__webglTexture ); - - } - - function setTexture3D( texture, slot ) { - - var textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - uploadTexture( textureProperties, texture, slot ); - return; - - } - - state.activeTexture( 33984 + slot ); - state.bindTexture( 32879, textureProperties.__webglTexture ); - - } - - function setTextureCube( texture, slot ) { - - if ( texture.image.length !== 6 ) { return; } - - var textureProperties = properties.get( texture ); - - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - - initTexture( textureProperties, texture ); - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); - - _gl.pixelStorei( 37440, texture.flipY ); - - var isCompressed = ( texture && ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ) ); - var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); - - var cubeImage = []; - - for ( var i = 0; i < 6; i ++ ) { - - if ( ! isCompressed && ! isDataTexture ) { - - cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); - - } else { - - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; - - } - - } - - var image = cubeImage[ 0 ], - supportsMips = isPowerOfTwo( image ) || isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - setTextureParameters( 34067, texture, supportsMips ); - - var mipmaps; - - if ( isCompressed ) { - - for ( var i = 0; i < 6; i ++ ) { - - mipmaps = cubeImage[ i ].mipmaps; - - for ( var j = 0; j < mipmaps.length; j ++ ) { - - var mipmap = mipmaps[ j ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( glFormat !== null ) { - - state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); - - } - - } else { - - state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - mipmaps = texture.mipmaps; - - for ( var i = 0; i < 6; i ++ ) { - - if ( isDataTexture ) { - - state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - - for ( var j = 0; j < mipmaps.length; j ++ ) { - - var mipmap = mipmaps[ j ]; - var mipmapImage = mipmap.image[ i ].image; - - state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); - - } - - } else { - - state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); - - for ( var j = 0; j < mipmaps.length; j ++ ) { - - var mipmap = mipmaps[ j ]; - - state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); - - } - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length; - - } - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - // We assume images for cube map have the same size. - generateMipmap( 34067, texture, image.width, image.height ); - - } - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) { texture.onUpdate( texture ); } - - } else { - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); - - } - - } - - function setTextureCubeDynamic( texture, slot ) { - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, properties.get( texture ).__webglTexture ); - - } - - var wrappingToGL = {}; - wrappingToGL[ RepeatWrapping ] = 10497; - wrappingToGL[ ClampToEdgeWrapping ] = 33071; - wrappingToGL[ MirroredRepeatWrapping ] = 33648; - - var filterToGL = {}; - filterToGL[ NearestFilter ] = 9728; - filterToGL[ NearestMipmapNearestFilter ] = 9984; - filterToGL[ NearestMipmapLinearFilter ] = 9986; - filterToGL[ LinearFilter ] = 9729; - filterToGL[ LinearMipmapNearestFilter ] = 9985; - filterToGL[ LinearMipmapLinearFilter ] = 9987; - - function setTextureParameters( textureType, texture, supportsMips ) { - - if ( supportsMips ) { - - _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); - _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); - - if ( textureType === 32879 || textureType === 35866 ) { - - _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); - - } - - _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); - _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); - - } else { - - _gl.texParameteri( textureType, 10242, 33071 ); - _gl.texParameteri( textureType, 10243, 33071 ); - - if ( textureType === 32879 || textureType === 35866 ) { - - _gl.texParameteri( textureType, 32882, 33071 ); - - } - - if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); - - } - - _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); - - if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { - - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); - - } - - } - - var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( extension ) { - - if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) { return; } - if ( texture.type === HalfFloatType && ( isWebGL2 || extensions.get( 'OES_texture_half_float_linear' ) ) === null ) { return; } - - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; - - } - - } - - } - - function initTexture( textureProperties, texture ) { - - if ( textureProperties.__webglInit === undefined ) { - - textureProperties.__webglInit = true; - - texture.addEventListener( 'dispose', onTextureDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - info.memory.textures ++; - - } - - } - - function uploadTexture( textureProperties, texture, slot ) { - - var textureType = 3553; - - if ( texture.isDataTexture2DArray ) { textureType = 35866; } - if ( texture.isDataTexture3D ) { textureType = 32879; } - - initTexture( textureProperties, texture ); - - state.activeTexture( 33984 + slot ); - state.bindTexture( textureType, textureProperties.__webglTexture ); - - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); - - var needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false; - var image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); - - var supportsMips = isPowerOfTwo( image ) || isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - - setTextureParameters( textureType, texture, supportsMips ); - - var mipmap, mipmaps = texture.mipmaps; - - if ( texture.isDepthTexture ) { - - // populate depth texture with dummy data - - glInternalFormat = 6402; - - if ( isWebGL2 ) { - - if ( texture.type === FloatType ) { - - glInternalFormat = 36012; - - } else if ( texture.type === UnsignedIntType ) { - - glInternalFormat = 33190; - - } else if ( texture.type === UnsignedInt248Type ) { - - glInternalFormat = 35056; - - } else { - - glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D - - } - - } else { - - if ( texture.type === FloatType ) { - - console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); - - } - - } - - // validation checks for WebGL 1 - - if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); - - texture.type = UnsignedShortType; - glType = utils.convert( texture.type ); - - } - - } - - if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { - - // Depth stencil textures need the DEPTH_STENCIL internal format - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - glInternalFormat = 34041; - - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedInt248Type ) { - - console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); - - texture.type = UnsignedInt248Type; - glType = utils.convert( texture.type ); - - } - - } - - // - - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); - - } else if ( texture.isDataTexture ) { - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && supportsMips ) { - - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } - - } else if ( texture.isCompressedTexture ) { - - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { - - if ( glFormat !== null ) { - - state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - - } else { - - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); - - } - - } else { - - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } - - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else if ( texture.isDataTexture2DArray ) { - - state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } else if ( texture.isDataTexture3D ) { - - state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; - - } else { - - // regular Texture (image, video, canvas) - - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels - - if ( mipmaps.length > 0 && supportsMips ) { - - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); - - } - - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; - - } else { - - state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - textureProperties.__maxMipLevel = 0; - - } - - } - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - generateMipmap( textureType, texture, image.width, image.height ); - - } - - textureProperties.__version = texture.version; - - if ( texture.onUpdate ) { texture.onUpdate( texture ); } - - } - - // Render targets - - // Setup storage for target texture and bind it to correct framebuffer - function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { - - var glFormat = utils.convert( renderTarget.texture.format ); - var glType = utils.convert( renderTarget.texture.type ); - var glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - _gl.bindFramebuffer( 36160, framebuffer ); - _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); - _gl.bindFramebuffer( 36160, null ); - - } - - // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - - _gl.bindRenderbuffer( 36161, renderbuffer ); - - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - - var glInternalFormat = 33189; - - if ( isMultisample ) { - - var depthTexture = renderTarget.depthTexture; - - if ( depthTexture && depthTexture.isDepthTexture ) { - - if ( depthTexture.type === FloatType ) { - - glInternalFormat = 36012; - - } else if ( depthTexture.type === UnsignedIntType ) { - - glInternalFormat = 33190; - - } - - } - - var samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - - } - - _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); - - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - - if ( isMultisample ) { - - var samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); - - } - - - _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); - - } else { - - var glFormat = utils.convert( renderTarget.texture.format ); - var glType = utils.convert( renderTarget.texture.type ); - var glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - - if ( isMultisample ) { - - var samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - } else { - - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - - } - - } - - _gl.bindRenderbuffer( 36161, null ); - - } - - // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { - - var isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); - if ( isCube ) { throw new Error( 'Depth Texture with cube render targets is not supported' ); } - - _gl.bindFramebuffer( 36160, framebuffer ); - - if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { - - throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); - - } - - // upload an empty depth texture with framebuffer size - if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || - renderTarget.depthTexture.image.width !== renderTarget.width || - renderTarget.depthTexture.image.height !== renderTarget.height ) { - - renderTarget.depthTexture.image.width = renderTarget.width; - renderTarget.depthTexture.image.height = renderTarget.height; - renderTarget.depthTexture.needsUpdate = true; - - } - - setTexture2D( renderTarget.depthTexture, 0 ); - - var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; - - if ( renderTarget.depthTexture.format === DepthFormat ) { - - _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); - - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - - _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); - - } else { - - throw new Error( 'Unknown depthTexture format' ); - - } - - } - - // Setup GL resources for a non-texture depth buffer - function setupDepthRenderbuffer( renderTarget ) { - - var renderTargetProperties = properties.get( renderTarget ); - - var isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - - if ( renderTarget.depthTexture ) { - - if ( isCube ) { throw new Error( 'target.depthTexture not supported in Cube render targets' ); } - - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - - } else { - - if ( isCube ) { - - renderTargetProperties.__webglDepthbuffer = []; - - for ( var i = 0; i < 6; i ++ ) { - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); - renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); - - } - - } else { - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); - renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); - - } - - } - - _gl.bindFramebuffer( 36160, null ); - - } - - // Set up GL resources for the render target - function setupRenderTarget( renderTarget ) { - - var renderTargetProperties = properties.get( renderTarget ); - var textureProperties = properties.get( renderTarget.texture ); - - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - - textureProperties.__webglTexture = _gl.createTexture(); - - info.memory.textures ++; - - var isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - var isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); - var supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2; - - // Handles WebGL2 RGBFormat fallback - #18858 - - if ( isWebGL2 && renderTarget.texture.format === RGBFormat && ( renderTarget.texture.type === FloatType || renderTarget.texture.type === HalfFloatType ) ) { - - renderTarget.texture.format = RGBAFormat; - - console.warn( 'THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.' ); - - } - - // Setup framebuffer - - if ( isCube ) { - - renderTargetProperties.__webglFramebuffer = []; - - for ( var i = 0; i < 6; i ++ ) { - - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - - } - - } else { - - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); - - if ( isMultisample ) { - - if ( isWebGL2 ) { - - renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); - renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); - - _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); - - var glFormat = utils.convert( renderTarget.texture.format ); - var glType = utils.convert( renderTarget.texture.type ); - var glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType ); - var samples = getRenderTargetSamples( renderTarget ); - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); - _gl.bindRenderbuffer( 36161, null ); - - if ( renderTarget.depthBuffer ) { - - renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); - - } - - _gl.bindFramebuffer( 36160, null ); - - - } else { - - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - - } - - } - - } - - // Setup color buffer - - if ( isCube ) { - - state.bindTexture( 34067, textureProperties.__webglTexture ); - setTextureParameters( 34067, renderTarget.texture, supportsMips ); - - for ( var i = 0; i < 6; i ++ ) { - - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, 36064, 34069 + i ); - - } - - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { - - generateMipmap( 34067, renderTarget.texture, renderTarget.width, renderTarget.height ); - - } - - state.bindTexture( 34067, null ); - - } else { - - state.bindTexture( 3553, textureProperties.__webglTexture ); - setTextureParameters( 3553, renderTarget.texture, supportsMips ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 36064, 3553 ); - - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { - - generateMipmap( 3553, renderTarget.texture, renderTarget.width, renderTarget.height ); - - } - - state.bindTexture( 3553, null ); - - } - - // Setup depth and stencil buffers - - if ( renderTarget.depthBuffer ) { - - setupDepthRenderbuffer( renderTarget ); - - } - - } - - function updateRenderTargetMipmap( renderTarget ) { - - var texture = renderTarget.texture; - var supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2; - - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - - var target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; - var webglTexture = properties.get( texture ).__webglTexture; - - state.bindTexture( target, webglTexture ); - generateMipmap( target, texture, renderTarget.width, renderTarget.height ); - state.bindTexture( target, null ); - - } - - } - - function updateMultisampleRenderTarget( renderTarget ) { - - if ( renderTarget.isWebGLMultisampleRenderTarget ) { - - if ( isWebGL2 ) { - - var renderTargetProperties = properties.get( renderTarget ); - - _gl.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); - - var width = renderTarget.width; - var height = renderTarget.height; - var mask = 16384; - - if ( renderTarget.depthBuffer ) { mask |= 256; } - if ( renderTarget.stencilBuffer ) { mask |= 1024; } - - _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); - - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); // see #18905 - - } else { - - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); - - } - - } - - } - - function getRenderTargetSamples( renderTarget ) { - - return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? - Math.min( maxSamples, renderTarget.samples ) : 0; - - } - - function updateVideoTexture( texture ) { - - var frame = info.render.frame; - - // Check the last frame we updated the VideoTexture - - if ( _videoTextures.get( texture ) !== frame ) { - - _videoTextures.set( texture, frame ); - texture.update(); - - } - - } - - // backwards compatibility - - var warnedTexture2D = false; - var warnedTextureCube = false; - - function safeSetTexture2D( texture, slot ) { - - if ( texture && texture.isWebGLRenderTarget ) { - - if ( warnedTexture2D === false ) { - - console.warn( "THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead." ); - warnedTexture2D = true; - - } - - texture = texture.texture; - - } - - setTexture2D( texture, slot ); - - } - - function safeSetTextureCube( texture, slot ) { - - if ( texture && texture.isWebGLCubeRenderTarget ) { - - if ( warnedTextureCube === false ) { - - console.warn( "THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); - warnedTextureCube = true; - - } - - texture = texture.texture; - - } - - // currently relying on the fact that WebGLCubeRenderTarget.texture is a Texture and NOT a CubeTexture - // TODO: unify these code paths - if ( ( texture && texture.isCubeTexture ) || - ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { - - // CompressedTexture can have Array in image :/ - - // this function alone should take care of cube textures - setTextureCube( texture, slot ); - - } else { - - // assumed: texture property of THREE.WebGLCubeRenderTarget - setTextureCubeDynamic( texture, slot ); - - } - - } - - // - - this.allocateTextureUnit = allocateTextureUnit; - this.resetTextureUnits = resetTextureUnits; - - this.setTexture2D = setTexture2D; - this.setTexture2DArray = setTexture2DArray; - this.setTexture3D = setTexture3D; - this.setTextureCube = setTextureCube; - this.setTextureCubeDynamic = setTextureCubeDynamic; - this.setupRenderTarget = setupRenderTarget; - this.updateRenderTargetMipmap = updateRenderTargetMipmap; - this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; - - this.safeSetTexture2D = safeSetTexture2D; - this.safeSetTextureCube = safeSetTextureCube; - - } - - /** - * @author thespite / http://www.twitter.com/thespite - */ - - function WebGLUtils( gl, extensions, capabilities ) { - - var isWebGL2 = capabilities.isWebGL2; - - function convert( p ) { - - var extension; - - if ( p === UnsignedByteType ) { return 5121; } - if ( p === UnsignedShort4444Type ) { return 32819; } - if ( p === UnsignedShort5551Type ) { return 32820; } - if ( p === UnsignedShort565Type ) { return 33635; } - - if ( p === ByteType ) { return 5120; } - if ( p === ShortType ) { return 5122; } - if ( p === UnsignedShortType ) { return 5123; } - if ( p === IntType ) { return 5124; } - if ( p === UnsignedIntType ) { return 5125; } - if ( p === FloatType ) { return 5126; } - - if ( p === HalfFloatType ) { - - if ( isWebGL2 ) { return 5131; } - - extension = extensions.get( 'OES_texture_half_float' ); - - if ( extension !== null ) { - - return extension.HALF_FLOAT_OES; - - } else { - - return null; - - } - - } - - if ( p === AlphaFormat ) { return 6406; } - if ( p === RGBFormat ) { return 6407; } - if ( p === RGBAFormat ) { return 6408; } - if ( p === LuminanceFormat ) { return 6409; } - if ( p === LuminanceAlphaFormat ) { return 6410; } - if ( p === DepthFormat ) { return 6402; } - if ( p === DepthStencilFormat ) { return 34041; } - if ( p === RedFormat ) { return 6403; } - - // WebGL2 formats. - - if ( p === RedIntegerFormat ) { return 36244; } - if ( p === RGFormat ) { return 33319; } - if ( p === RGIntegerFormat ) { return 33320; } - if ( p === RGBIntegerFormat ) { return 36248; } - if ( p === RGBAIntegerFormat ) { return 36249; } - - if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || - p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - if ( extension !== null ) { - - if ( p === RGB_S3TC_DXT1_Format ) { return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; } - if ( p === RGBA_S3TC_DXT1_Format ) { return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; } - if ( p === RGBA_S3TC_DXT3_Format ) { return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; } - if ( p === RGBA_S3TC_DXT5_Format ) { return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } - - } else { - - return null; - - } - - } - - if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || - p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - if ( extension !== null ) { - - if ( p === RGB_PVRTC_4BPPV1_Format ) { return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; } - if ( p === RGB_PVRTC_2BPPV1_Format ) { return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; } - if ( p === RGBA_PVRTC_4BPPV1_Format ) { return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; } - if ( p === RGBA_PVRTC_2BPPV1_Format ) { return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } - - } else { - - return null; - - } - - } - - if ( p === RGB_ETC1_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - - if ( extension !== null ) { - - return extension.COMPRESSED_RGB_ETC1_WEBGL; - - } else { - - return null; - - } - - } - - if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_etc' ); - - if ( extension !== null ) { - - if ( p === RGB_ETC2_Format ) { return extension.COMPRESSED_RGB8_ETC2; } - if ( p === RGBA_ETC2_EAC_Format ) { return extension.COMPRESSED_RGBA8_ETC2_EAC; } - - } - - } - - if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || - p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || - p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || - p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || - p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format || - p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format || - p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format || - p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format || - p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format || - p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format ) { - - extension = extensions.get( 'WEBGL_compressed_texture_astc' ); - - if ( extension !== null ) { - - // TODO Complete? - - return p; - - } else { - - return null; - - } - - } - - if ( p === RGBA_BPTC_Format ) { - - extension = extensions.get( 'EXT_texture_compression_bptc' ); - - if ( extension !== null ) { - - // TODO Complete? - - return p; - - } else { - - return null; - - } - - } - - if ( p === UnsignedInt248Type ) { - - if ( isWebGL2 ) { return 34042; } - - extension = extensions.get( 'WEBGL_depth_texture' ); - - if ( extension !== null ) { - - return extension.UNSIGNED_INT_24_8_WEBGL; - - } else { - - return null; - - } - - } - - } - - return { convert: convert }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function ArrayCamera( array ) { - - PerspectiveCamera.call( this ); - - this.cameras = array || []; - - } - - ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), { - - constructor: ArrayCamera, - - isArrayCamera: true - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Group() { - - Object3D.call( this ); - - this.type = 'Group'; - - } - - Group.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Group, - - isGroup: true - - } ); - - /** - * @author Mugen87 / https://github.com/Mugen87 - */ - - function WebXRController() { - - this._targetRay = null; - this._grip = null; - - } - - Object.assign( WebXRController.prototype, { - - constructor: WebXRController, - - getTargetRaySpace: function () { - - if ( this._targetRay === null ) { - - this._targetRay = new Group(); - this._targetRay.matrixAutoUpdate = false; - this._targetRay.visible = false; - - } - - return this._targetRay; - - }, - - getGripSpace: function () { - - if ( this._grip === null ) { - - this._grip = new Group(); - this._grip.matrixAutoUpdate = false; - this._grip.visible = false; - - } - - return this._grip; - - }, - - dispatchEvent: function ( event ) { - - if ( this._targetRay !== null ) { - - this._targetRay.dispatchEvent( event ); - - } - - if ( this._grip !== null ) { - - this._grip.dispatchEvent( event ); - - } - - return this; - - }, - - disconnect: function ( inputSource ) { - - this.dispatchEvent( { type: 'disconnected', data: inputSource } ); - - if ( this._targetRay !== null ) { - - this._targetRay.visible = false; - - } - - if ( this._grip !== null ) { - - this._grip.visible = false; - - } - - return this; - - }, - - update: function ( inputSource, frame, referenceSpace ) { - - var inputPose = null; - var gripPose = null; - - var targetRay = this._targetRay; - var grip = this._grip; - - if ( inputSource ) { - - if ( targetRay !== null ) { - - inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); - - if ( inputPose !== null ) { - - targetRay.matrix.fromArray( inputPose.transform.matrix ); - targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); - - } - - } - - if ( grip !== null && inputSource.gripSpace ) { - - gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); - - if ( gripPose !== null ) { - - grip.matrix.fromArray( gripPose.transform.matrix ); - grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); - - } - - } - - } - - if ( targetRay !== null ) { - - targetRay.visible = ( inputPose !== null ); - - } - - if ( grip !== null ) { - - grip.visible = ( gripPose !== null ); - - } - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebXRManager( renderer, gl ) { - - var scope = this; - - var session = null; - - var framebufferScaleFactor = 1.0; - - var referenceSpace = null; - var referenceSpaceType = 'local-floor'; - - var pose = null; - - var controllers = []; - var inputSourcesMap = new Map(); - - // - - var cameraL = new PerspectiveCamera(); - cameraL.layers.enable( 1 ); - cameraL.viewport = new Vector4(); - - var cameraR = new PerspectiveCamera(); - cameraR.layers.enable( 2 ); - cameraR.viewport = new Vector4(); - - var cameras = [ cameraL, cameraR ]; - - var cameraVR = new ArrayCamera(); - cameraVR.layers.enable( 1 ); - cameraVR.layers.enable( 2 ); - - var _currentDepthNear = null; - var _currentDepthFar = null; - - // - - this.enabled = false; - - this.isPresenting = false; - - this.getController = function ( index ) { - - var controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getTargetRaySpace(); - - }; - - this.getControllerGrip = function ( index ) { - - var controller = controllers[ index ]; - - if ( controller === undefined ) { - - controller = new WebXRController(); - controllers[ index ] = controller; - - } - - return controller.getGripSpace(); - - }; - - // - - function onSessionEvent( event ) { - - var controller = inputSourcesMap.get( event.inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: event.type } ); - - } - - } - - function onSessionEnd() { - - inputSourcesMap.forEach( function ( controller, inputSource ) { - - controller.disconnect( inputSource ); - - } ); - - inputSourcesMap.clear(); - - // - - renderer.setFramebuffer( null ); - renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830 - animation.stop(); - - scope.isPresenting = false; - - scope.dispatchEvent( { type: 'sessionend' } ); - - } - - function onRequestReferenceSpace( value ) { - - referenceSpace = value; - - animation.setContext( session ); - animation.start(); - - scope.isPresenting = true; - - scope.dispatchEvent( { type: 'sessionstart' } ); - - } - - this.setFramebufferScaleFactor = function ( value ) { - - framebufferScaleFactor = value; - - if ( scope.isPresenting === true ) { - - console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); - - } - - }; - - this.setReferenceSpaceType = function ( value ) { - - referenceSpaceType = value; - - if ( scope.isPresenting === true ) { - - console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); - - } - - }; - - this.getReferenceSpace = function () { - - return referenceSpace; - - }; - - this.getSession = function () { - - return session; - - }; - - this.setSession = function ( value ) { - - session = value; - - if ( session !== null ) { - - session.addEventListener( 'select', onSessionEvent ); - session.addEventListener( 'selectstart', onSessionEvent ); - session.addEventListener( 'selectend', onSessionEvent ); - session.addEventListener( 'squeeze', onSessionEvent ); - session.addEventListener( 'squeezestart', onSessionEvent ); - session.addEventListener( 'squeezeend', onSessionEvent ); - session.addEventListener( 'end', onSessionEnd ); - - var attributes = gl.getContextAttributes(); - - if ( attributes.xrCompatible !== true ) { - - gl.makeXRCompatible(); - - } - - var layerInit = { - antialias: attributes.antialias, - alpha: attributes.alpha, - depth: attributes.depth, - stencil: attributes.stencil, - framebufferScaleFactor: framebufferScaleFactor - }; - - // eslint-disable-next-line no-undef - var baseLayer = new XRWebGLLayer( session, gl, layerInit ); - - session.updateRenderState( { baseLayer: baseLayer } ); - - session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace ); - - // - - session.addEventListener( 'inputsourceschange', updateInputSources ); - - } - - }; - - function updateInputSources( event ) { - - var inputSources = session.inputSources; - - // Assign inputSources to available controllers - - for ( var i = 0; i < controllers.length; i ++ ) { - - inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); - - } - - // Notify disconnected - - for ( var i = 0; i < event.removed.length; i ++ ) { - - var inputSource = event.removed[ i ]; - var controller = inputSourcesMap.get( inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); - inputSourcesMap.delete( inputSource ); - - } - - } - - // Notify connected - - for ( var i = 0; i < event.added.length; i ++ ) { - - var inputSource = event.added[ i ]; - var controller = inputSourcesMap.get( inputSource ); - - if ( controller ) { - - controller.dispatchEvent( { type: 'connected', data: inputSource } ); - - } - - } - - } - - // - - var cameraLPos = new Vector3(); - var cameraRPos = new Vector3(); - - /** - * @author jsantell / https://www.jsantell.com/ - * - * Assumes 2 cameras that are parallel and share an X-axis, and that - * the cameras' projection and world matrices have already been set. - * And that near and far planes are identical for both cameras. - * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 - */ - function setProjectionFromUnion( camera, cameraL, cameraR ) { - - cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); - cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); - - var ipd = cameraLPos.distanceTo( cameraRPos ); - - var projL = cameraL.projectionMatrix.elements; - var projR = cameraR.projectionMatrix.elements; - - // VR systems will have identical far and near planes, and - // most likely identical top and bottom frustum extents. - // Use the left camera for these values. - var near = projL[ 14 ] / ( projL[ 10 ] - 1 ); - var far = projL[ 14 ] / ( projL[ 10 ] + 1 ); - var topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; - var bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; - - var leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; - var rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; - var left = near * leftFov; - var right = near * rightFov; - - // Calculate the new camera's position offset from the - // left camera. xOffset should be roughly half `ipd`. - var zOffset = ipd / ( - leftFov + rightFov ); - var xOffset = zOffset * - leftFov; - - // TODO: Better way to apply this offset? - cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); - camera.translateX( xOffset ); - camera.translateZ( zOffset ); - camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - - // Find the union of the frustum values of the cameras and scale - // the values so that the near plane's position does not change in world space, - // although must now be relative to the new union camera. - var near2 = near + zOffset; - var far2 = far + zOffset; - var left2 = left - xOffset; - var right2 = right + ( ipd - xOffset ); - var top2 = topFov * far / far2 * near2; - var bottom2 = bottomFov * far / far2 * near2; - - camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); - - } - - function updateCamera( camera, parent ) { - - if ( parent === null ) { - - camera.matrixWorld.copy( camera.matrix ); - - } else { - - camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); - - } - - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - - } - - this.getCamera = function ( camera ) { - - cameraVR.near = cameraR.near = cameraL.near = camera.near; - cameraVR.far = cameraR.far = cameraL.far = camera.far; - - if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { - - // Note that the new renderState won't apply until the next frame. See #18320 - - session.updateRenderState( { - depthNear: cameraVR.near, - depthFar: cameraVR.far - } ); - - _currentDepthNear = cameraVR.near; - _currentDepthFar = cameraVR.far; - - } - - var parent = camera.parent; - var cameras = cameraVR.cameras; - - updateCamera( cameraVR, parent ); - - for ( var i = 0; i < cameras.length; i ++ ) { - - updateCamera( cameras[ i ], parent ); - - } - - // update camera and its children - - camera.matrixWorld.copy( cameraVR.matrixWorld ); - - var children = camera.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - children[ i ].updateMatrixWorld( true ); - - } - - // update projection matrix for proper view frustum culling - - if ( cameras.length === 2 ) { - - setProjectionFromUnion( cameraVR, cameraL, cameraR ); - - } else { - - // assume single camera setup (AR) - - cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); - - } - - return cameraVR; - - }; - - // Animation Loop - - var onAnimationFrameCallback = null; - - function onAnimationFrame( time, frame ) { - - pose = frame.getViewerPose( referenceSpace ); - - if ( pose !== null ) { - - var views = pose.views; - var baseLayer = session.renderState.baseLayer; - - renderer.setFramebuffer( baseLayer.framebuffer ); - - var cameraVRNeedsUpdate = false; - - // check if it's necessary to rebuild cameraVR's camera list - - if ( views.length !== cameraVR.cameras.length ) { - - cameraVR.cameras.length = 0; - cameraVRNeedsUpdate = true; - - } - - for ( var i = 0; i < views.length; i ++ ) { - - var view = views[ i ]; - var viewport = baseLayer.getViewport( view ); - - var camera = cameras[ i ]; - camera.matrix.fromArray( view.transform.matrix ); - camera.projectionMatrix.fromArray( view.projectionMatrix ); - camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); - - if ( i === 0 ) { - - cameraVR.matrix.copy( camera.matrix ); - - } - - if ( cameraVRNeedsUpdate === true ) { - - cameraVR.cameras.push( camera ); - - } - - } - - } - - // - - var inputSources = session.inputSources; - - for ( var i = 0; i < controllers.length; i ++ ) { - - var controller = controllers[ i ]; - var inputSource = inputSources[ i ]; - - controller.update( inputSource, frame, referenceSpace ); - - } - - if ( onAnimationFrameCallback ) { onAnimationFrameCallback( time, frame ); } - - } - - var animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); - - this.setAnimationLoop = function ( callback ) { - - onAnimationFrameCallback = callback; - - }; - - this.dispose = function () {}; - - } - - Object.assign( WebXRManager.prototype, EventDispatcher.prototype ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLMaterials( properties ) { - - function refreshFogUniforms( uniforms, fog ) { - - uniforms.fogColor.value.copy( fog.color ); - - if ( fog.isFog ) { - - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; - - } else if ( fog.isFogExp2 ) { - - uniforms.fogDensity.value = fog.density; - - } - - } - - function refreshMaterialUniforms( uniforms, material, environment, pixelRatio, height ) { - - if ( material.isMeshBasicMaterial ) { - - refreshUniformsCommon( uniforms, material ); - - } else if ( material.isMeshLambertMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsLambert( uniforms, material ); - - } else if ( material.isMeshToonMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsToon( uniforms, material ); - - } else if ( material.isMeshPhongMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsPhong( uniforms, material ); - - } else if ( material.isMeshStandardMaterial ) { - - refreshUniformsCommon( uniforms, material, environment ); - - if ( material.isMeshPhysicalMaterial ) { - - refreshUniformsPhysical( uniforms, material, environment ); - - } else { - - refreshUniformsStandard( uniforms, material, environment ); - - } - - } else if ( material.isMeshMatcapMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsMatcap( uniforms, material ); - - } else if ( material.isMeshDepthMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsDepth( uniforms, material ); - - } else if ( material.isMeshDistanceMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsDistance( uniforms, material ); - - } else if ( material.isMeshNormalMaterial ) { - - refreshUniformsCommon( uniforms, material ); - refreshUniformsNormal( uniforms, material ); - - } else if ( material.isLineBasicMaterial ) { - - refreshUniformsLine( uniforms, material ); - - if ( material.isLineDashedMaterial ) { - - refreshUniformsDash( uniforms, material ); - - } - - } else if ( material.isPointsMaterial ) { - - refreshUniformsPoints( uniforms, material, pixelRatio, height ); - - } else if ( material.isSpriteMaterial ) { - - refreshUniformsSprites( uniforms, material ); - - } else if ( material.isShadowMaterial ) { - - uniforms.color.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - - } else if ( material.isShaderMaterial ) { - - material.uniformsNeedUpdate = false; // #15581 - - } - - } - - function refreshUniformsCommon( uniforms, material, environment ) { - - uniforms.opacity.value = material.opacity; - - if ( material.color ) { - - uniforms.diffuse.value.copy( material.color ); - - } - - if ( material.emissive ) { - - uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); - - } - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - if ( material.specularMap ) { - - uniforms.specularMap.value = material.specularMap; - - } - - var envMap = material.envMap || environment; - - if ( envMap ) { - - uniforms.envMap.value = envMap; - - uniforms.flipEnvMap.value = envMap.isCubeTexture ? - 1 : 1; - - uniforms.reflectivity.value = material.reflectivity; - uniforms.refractionRatio.value = material.refractionRatio; - - uniforms.maxMipLevel.value = properties.get( envMap ).__maxMipLevel; - - } - - if ( material.lightMap ) { - - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; - - } - - if ( material.aoMap ) { - - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. normal map - // 4. bump map - // 5. alpha map - // 6. emissive map - - var uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.specularMap ) { - - uvScaleMap = material.specularMap; - - } else if ( material.displacementMap ) { - - uvScaleMap = material.displacementMap; - - } else if ( material.normalMap ) { - - uvScaleMap = material.normalMap; - - } else if ( material.bumpMap ) { - - uvScaleMap = material.bumpMap; - - } else if ( material.roughnessMap ) { - - uvScaleMap = material.roughnessMap; - - } else if ( material.metalnessMap ) { - - uvScaleMap = material.metalnessMap; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } else if ( material.emissiveMap ) { - - uvScaleMap = material.emissiveMap; - - } - - if ( uvScaleMap !== undefined ) { - - // backwards compatibility - if ( uvScaleMap.isWebGLRenderTarget ) { - - uvScaleMap = uvScaleMap.texture; - - } - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - // uv repeat and offset setting priorities for uv2 - // 1. ao map - // 2. light map - - var uv2ScaleMap; - - if ( material.aoMap ) { - - uv2ScaleMap = material.aoMap; - - } else if ( material.lightMap ) { - - uv2ScaleMap = material.lightMap; - - } - - if ( uv2ScaleMap !== undefined ) { - - // backwards compatibility - if ( uv2ScaleMap.isWebGLRenderTarget ) { - - uv2ScaleMap = uv2ScaleMap.texture; - - } - - if ( uv2ScaleMap.matrixAutoUpdate === true ) { - - uv2ScaleMap.updateMatrix(); - - } - - uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); - - } - - } - - function refreshUniformsLine( uniforms, material ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - - } - - function refreshUniformsDash( uniforms, material ) { - - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; - - } - - function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size * pixelRatio; - uniforms.scale.value = height * 0.5; - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map - - var uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } - - if ( uvScaleMap !== undefined ) { - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - } - - function refreshUniformsSprites( uniforms, material ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.rotation.value = material.rotation; - - if ( material.map ) { - - uniforms.map.value = material.map; - - } - - if ( material.alphaMap ) { - - uniforms.alphaMap.value = material.alphaMap; - - } - - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map - - var uvScaleMap; - - if ( material.map ) { - - uvScaleMap = material.map; - - } else if ( material.alphaMap ) { - - uvScaleMap = material.alphaMap; - - } - - if ( uvScaleMap !== undefined ) { - - if ( uvScaleMap.matrixAutoUpdate === true ) { - - uvScaleMap.updateMatrix(); - - } - - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - - } - - } - - function refreshUniformsLambert( uniforms, material ) { - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - } - - function refreshUniformsPhong( uniforms, material ) { - - uniforms.specular.value.copy( material.specular ); - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; } - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); } - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsToon( uniforms, material ) { - - uniforms.specular.value.copy( material.specular ); - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - - if ( material.gradientMap ) { - - uniforms.gradientMap.value = material.gradientMap; - - } - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; } - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); } - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsStandard( uniforms, material, environment ) { - - uniforms.roughness.value = material.roughness; - uniforms.metalness.value = material.metalness; - - if ( material.roughnessMap ) { - - uniforms.roughnessMap.value = material.roughnessMap; - - } - - if ( material.metalnessMap ) { - - uniforms.metalnessMap.value = material.metalnessMap; - - } - - if ( material.emissiveMap ) { - - uniforms.emissiveMap.value = material.emissiveMap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; } - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); } - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - if ( material.envMap || environment ) { - - //uniforms.envMap.value = material.envMap; // part of uniforms common - uniforms.envMapIntensity.value = material.envMapIntensity; - - } - - } - - function refreshUniformsPhysical( uniforms, material, environment ) { - - refreshUniformsStandard( uniforms, material, environment ); - - uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common - - uniforms.clearcoat.value = material.clearcoat; - uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - if ( material.sheen ) { uniforms.sheen.value.copy( material.sheen ); } - - if ( material.clearcoatMap ) { - - uniforms.clearcoatMap.value = material.clearcoatMap; - - } - - if ( material.clearcoatRoughnessMap ) { - - uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; - - } - - if ( material.clearcoatNormalMap ) { - - uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); - uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; - - if ( material.side === BackSide ) { - - uniforms.clearcoatNormalScale.value.negate(); - - } - - } - - uniforms.transparency.value = material.transparency; - - } - - function refreshUniformsMatcap( uniforms, material ) { - - if ( material.matcap ) { - - uniforms.matcap.value = material.matcap; - - } - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; } - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); } - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsDepth( uniforms, material ) { - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - function refreshUniformsDistance( uniforms, material ) { - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - uniforms.referencePosition.value.copy( material.referencePosition ); - uniforms.nearDistance.value = material.nearDistance; - uniforms.farDistance.value = material.farDistance; - - } - - function refreshUniformsNormal( uniforms, material ) { - - if ( material.bumpMap ) { - - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) { uniforms.bumpScale.value *= - 1; } - - } - - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) { uniforms.normalScale.value.negate(); } - - } - - if ( material.displacementMap ) { - - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; - - } - - } - - return { - refreshFogUniforms: refreshFogUniforms, - refreshMaterialUniforms: refreshMaterialUniforms - }; - - } - - /** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - * @author tschw - */ - - function WebGLRenderer( parameters ) { - - parameters = parameters || {}; - - var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), - _context = parameters.context !== undefined ? parameters.context : null, - - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', - _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; - - var currentRenderList = null; - var currentRenderState = null; - - // public properties - - this.domElement = _canvas; - - // Debug configuration container - this.debug = { - - /** - * Enables error checking and reporting when shader programs are being compiled - * @type {boolean} - */ - checkShaderErrors: true - }; - - // clearing - - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; - - // scene graph - - this.sortObjects = true; - - // user-defined clipping - - this.clippingPlanes = []; - this.localClippingEnabled = false; - - // physically based shading - - this.gammaFactor = 2.0; // for backwards compatibility - this.outputEncoding = LinearEncoding; - - // physical lights - - this.physicallyCorrectLights = false; - - // tone mapping - - this.toneMapping = NoToneMapping; - this.toneMappingExposure = 1.0; - this.toneMappingWhitePoint = 1.0; - - // morphs - - this.maxMorphTargets = 8; - this.maxMorphNormals = 4; - - // internal properties - - var _this = this, - - _isContextLost = false, - - // internal state cache - - _framebuffer = null, - - _currentActiveCubeFace = 0, - _currentActiveMipmapLevel = 0, - _currentRenderTarget = null, - _currentFramebuffer = null, - _currentMaterialId = - 1, - - // geometry and program caching - - _currentGeometryProgram = { - geometry: null, - program: null, - wireframe: false - }, - - _currentCamera = null, - _currentArrayCamera = null, - - _currentViewport = new Vector4(), - _currentScissor = new Vector4(), - _currentScissorTest = null, - - // - - _width = _canvas.width, - _height = _canvas.height, - - _pixelRatio = 1, - _opaqueSort = null, - _transparentSort = null, - - _viewport = new Vector4( 0, 0, _width, _height ), - _scissor = new Vector4( 0, 0, _width, _height ), - _scissorTest = false, - - // frustum - - _frustum = new Frustum(), - - // clipping - - _clipping = new WebGLClipping(), - _clippingEnabled = false, - _localClippingEnabled = false, - - // camera matrices cache - - _projScreenMatrix = new Matrix4(), - - _vector3 = new Vector3(); - - function getTargetPixelRatio() { - - return _currentRenderTarget === null ? _pixelRatio : 1; - - } - - // initialize - - var _gl; - - try { - - var contextAttributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer, - powerPreference: _powerPreference, - failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat - }; - - // event listeners must be registered before WebGL context is created, see #12753 - - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); - - _gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes ); - - if ( _gl === null ) { - - if ( _canvas.getContext( 'webgl' ) !== null ) { - - throw new Error( 'Error creating WebGL context with your selected attributes.' ); - - } else { - - throw new Error( 'Error creating WebGL context.' ); - - } - - } - - // Some experimental-webgl implementations do not have getShaderPrecisionFormat - - if ( _gl.getShaderPrecisionFormat === undefined ) { - - _gl.getShaderPrecisionFormat = function () { - - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - - }; - - } - - } catch ( error ) { - - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; - - } - - var extensions, capabilities, state, info; - var properties, textures, attributes, geometries, objects; - var programCache, materials, renderLists, renderStates; - - var background, morphtargets, bufferRenderer, indexedBufferRenderer; - - var utils; - - function initGLContext() { - - extensions = new WebGLExtensions( _gl ); - - capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - - if ( capabilities.isWebGL2 === false ) { - - extensions.get( 'WEBGL_depth_texture' ); - extensions.get( 'OES_texture_float' ); - extensions.get( 'OES_texture_half_float' ); - extensions.get( 'OES_texture_half_float_linear' ); - extensions.get( 'OES_standard_derivatives' ); - extensions.get( 'OES_element_index_uint' ); - extensions.get( 'ANGLE_instanced_arrays' ); - - } - - extensions.get( 'OES_texture_float_linear' ); - - utils = new WebGLUtils( _gl, extensions, capabilities ); - - state = new WebGLState( _gl, extensions, capabilities ); - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - - info = new WebGLInfo( _gl ); - properties = new WebGLProperties(); - textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - attributes = new WebGLAttributes( _gl, capabilities ); - geometries = new WebGLGeometries( _gl, attributes, info ); - objects = new WebGLObjects( _gl, geometries, attributes, info ); - morphtargets = new WebGLMorphtargets( _gl ); - programCache = new WebGLPrograms( _this, extensions, capabilities ); - materials = new WebGLMaterials( properties ); - renderLists = new WebGLRenderLists(); - renderStates = new WebGLRenderStates(); - - background = new WebGLBackground( _this, state, objects, _premultipliedAlpha ); - - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); - - info.programs = programCache.programs; - - _this.capabilities = capabilities; - _this.extensions = extensions; - _this.properties = properties; - _this.renderLists = renderLists; - _this.state = state; - _this.info = info; - - } - - initGLContext(); - - // xr - - var xr = new WebXRManager( _this, _gl ); - - this.xr = xr; - - // shadow map - - var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); - - this.shadowMap = shadowMap; - - // API - - this.getContext = function () { - - return _gl; - - }; - - this.getContextAttributes = function () { - - return _gl.getContextAttributes(); - - }; - - this.forceContextLoss = function () { - - var extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) { extension.loseContext(); } - - }; - - this.forceContextRestore = function () { - - var extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) { extension.restoreContext(); } - - }; - - this.getPixelRatio = function () { - - return _pixelRatio; - - }; - - this.setPixelRatio = function ( value ) { - - if ( value === undefined ) { return; } - - _pixelRatio = value; - - this.setSize( _width, _height, false ); - - }; - - this.getSize = function ( target ) { - - if ( target === undefined ) { - - console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' ); - - target = new Vector2(); - - } - - return target.set( _width, _height ); - - }; - - this.setSize = function ( width, height, updateStyle ) { - - if ( xr.isPresenting ) { - - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); - return; - - } - - _width = width; - _height = height; - - _canvas.width = Math.floor( width * _pixelRatio ); - _canvas.height = Math.floor( height * _pixelRatio ); - - if ( updateStyle !== false ) { - - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; - - } - - this.setViewport( 0, 0, width, height ); - - }; - - this.getDrawingBufferSize = function ( target ) { - - if ( target === undefined ) { - - console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' ); - - target = new Vector2(); - - } - - return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - - }; - - this.setDrawingBufferSize = function ( width, height, pixelRatio ) { - - _width = width; - _height = height; - - _pixelRatio = pixelRatio; - - _canvas.width = Math.floor( width * pixelRatio ); - _canvas.height = Math.floor( height * pixelRatio ); - - this.setViewport( 0, 0, width, height ); - - }; - - this.getCurrentViewport = function ( target ) { - - if ( target === undefined ) { - - console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' ); - - target = new Vector4(); - - } - - return target.copy( _currentViewport ); - - }; - - this.getViewport = function ( target ) { - - return target.copy( _viewport ); - - }; - - this.setViewport = function ( x, y, width, height ) { - - if ( x.isVector4 ) { - - _viewport.set( x.x, x.y, x.z, x.w ); - - } else { - - _viewport.set( x, y, width, height ); - - } - - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - - }; - - this.getScissor = function ( target ) { - - return target.copy( _scissor ); - - }; - - this.setScissor = function ( x, y, width, height ) { - - if ( x.isVector4 ) { - - _scissor.set( x.x, x.y, x.z, x.w ); - - } else { - - _scissor.set( x, y, width, height ); - - } - - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - - }; - - this.getScissorTest = function () { - - return _scissorTest; - - }; - - this.setScissorTest = function ( boolean ) { - - state.setScissorTest( _scissorTest = boolean ); - - }; - - this.setOpaqueSort = function ( method ) { - - _opaqueSort = method; - - }; - - this.setTransparentSort = function ( method ) { - - _transparentSort = method; - - }; - - // Clearing - - this.getClearColor = function () { - - return background.getClearColor(); - - }; - - this.setClearColor = function () { - - background.setClearColor.apply( background, arguments ); - - }; - - this.getClearAlpha = function () { - - return background.getClearAlpha(); - - }; - - this.setClearAlpha = function () { - - background.setClearAlpha.apply( background, arguments ); - - }; - - this.clear = function ( color, depth, stencil ) { - - var bits = 0; - - if ( color === undefined || color ) { bits |= 16384; } - if ( depth === undefined || depth ) { bits |= 256; } - if ( stencil === undefined || stencil ) { bits |= 1024; } - - _gl.clear( bits ); - - }; - - this.clearColor = function () { - - this.clear( true, false, false ); - - }; - - this.clearDepth = function () { - - this.clear( false, true, false ); - - }; - - this.clearStencil = function () { - - this.clear( false, false, true ); - - }; - - // - - this.dispose = function () { - - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); - - renderLists.dispose(); - renderStates.dispose(); - properties.dispose(); - objects.dispose(); - - xr.dispose(); - - animation.stop(); - - }; - - // Events - - function onContextLost( event ) { - - event.preventDefault(); - - console.log( 'THREE.WebGLRenderer: Context Lost.' ); - - _isContextLost = true; - - } - - function onContextRestore( /* event */ ) { - - console.log( 'THREE.WebGLRenderer: Context Restored.' ); - - _isContextLost = false; - - initGLContext(); - - } - - function onMaterialDispose( event ) { - - var material = event.target; - - material.removeEventListener( 'dispose', onMaterialDispose ); - - deallocateMaterial( material ); - - } - - // Buffer deallocation - - function deallocateMaterial( material ) { - - releaseMaterialProgramReference( material ); - - properties.remove( material ); - - } - - - function releaseMaterialProgramReference( material ) { - - var programInfo = properties.get( material ).program; - - material.program = undefined; - - if ( programInfo !== undefined ) { - - programCache.releaseProgram( programInfo ); - - } - - } - - // Buffer rendering - - function renderObjectImmediate( object, program ) { - - object.render( function ( object ) { - - _this.renderBufferImmediate( object, program ); - - } ); - - } - - this.renderBufferImmediate = function ( object, program ) { - - state.initAttributes(); - - var buffers = properties.get( object ); - - if ( object.hasPositions && ! buffers.position ) { buffers.position = _gl.createBuffer(); } - if ( object.hasNormals && ! buffers.normal ) { buffers.normal = _gl.createBuffer(); } - if ( object.hasUvs && ! buffers.uv ) { buffers.uv = _gl.createBuffer(); } - if ( object.hasColors && ! buffers.color ) { buffers.color = _gl.createBuffer(); } - - var programAttributes = program.getAttributes(); - - if ( object.hasPositions ) { - - _gl.bindBuffer( 34962, buffers.position ); - _gl.bufferData( 34962, object.positionArray, 35048 ); - - state.enableAttribute( programAttributes.position ); - _gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 ); - - } - - if ( object.hasNormals ) { - - _gl.bindBuffer( 34962, buffers.normal ); - _gl.bufferData( 34962, object.normalArray, 35048 ); - - state.enableAttribute( programAttributes.normal ); - _gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 ); - - } - - if ( object.hasUvs ) { - - _gl.bindBuffer( 34962, buffers.uv ); - _gl.bufferData( 34962, object.uvArray, 35048 ); - - state.enableAttribute( programAttributes.uv ); - _gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 ); - - } - - if ( object.hasColors ) { - - _gl.bindBuffer( 34962, buffers.color ); - _gl.bufferData( 34962, object.colorArray, 35048 ); - - state.enableAttribute( programAttributes.color ); - _gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 ); - - } - - state.disableUnusedAttributes(); - - _gl.drawArrays( 4, 0, object.count ); - - object.count = 0; - - }; - - var tempScene = new Scene(); - - this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { - - if ( scene === null ) { scene = tempScene; } // renderBufferDirect second parameter used to be fog (could be null) - - var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - - var program = setProgram( camera, scene, material, object ); - - state.setMaterial( material, frontFaceCW ); - - var updateBuffers = false; - - if ( _currentGeometryProgram.geometry !== geometry.id || - _currentGeometryProgram.program !== program.id || - _currentGeometryProgram.wireframe !== ( material.wireframe === true ) ) { - - _currentGeometryProgram.geometry = geometry.id; - _currentGeometryProgram.program = program.id; - _currentGeometryProgram.wireframe = material.wireframe === true; - updateBuffers = true; - - } - - if ( material.morphTargets || material.morphNormals ) { - - morphtargets.update( object, geometry, material, program ); - - updateBuffers = true; - - } - - if ( object.isInstancedMesh === true ) { - - updateBuffers = true; - - } - - // - - var index = geometry.index; - var position = geometry.attributes.position; - - // - - if ( index === null ) { - - if ( position === undefined || position.count === 0 ) { return; } - - } else if ( index.count === 0 ) { - - return; - - } - - // - - var rangeFactor = 1; - - if ( material.wireframe === true ) { - - index = geometries.getWireframeAttribute( geometry ); - rangeFactor = 2; - - } - - var attribute; - var renderer = bufferRenderer; - - if ( index !== null ) { - - attribute = attributes.get( index ); - - renderer = indexedBufferRenderer; - renderer.setIndex( attribute ); - - } - - if ( updateBuffers ) { - - setupVertexAttributes( object, geometry, material, program ); - - if ( index !== null ) { - - _gl.bindBuffer( 34963, attribute.buffer ); - - } - - } - - // - - var dataCount = ( index !== null ) ? index.count : position.count; - - var rangeStart = geometry.drawRange.start * rangeFactor; - var rangeCount = geometry.drawRange.count * rangeFactor; - - var groupStart = group !== null ? group.start * rangeFactor : 0; - var groupCount = group !== null ? group.count * rangeFactor : Infinity; - - var drawStart = Math.max( rangeStart, groupStart ); - var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; - - var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); - - if ( drawCount === 0 ) { return; } - - // - - if ( object.isMesh ) { - - if ( material.wireframe === true ) { - - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( 1 ); - - } else { - - renderer.setMode( 4 ); - - } - - } else if ( object.isLine ) { - - var lineWidth = material.linewidth; - - if ( lineWidth === undefined ) { lineWidth = 1; } // Not using Line*Material - - state.setLineWidth( lineWidth * getTargetPixelRatio() ); - - if ( object.isLineSegments ) { - - renderer.setMode( 1 ); - - } else if ( object.isLineLoop ) { - - renderer.setMode( 2 ); - - } else { - - renderer.setMode( 3 ); - - } - - } else if ( object.isPoints ) { - - renderer.setMode( 0 ); - - } else if ( object.isSprite ) { - - renderer.setMode( 4 ); - - } - - if ( object.isInstancedMesh ) { - - renderer.renderInstances( geometry, drawStart, drawCount, object.count ); - - } else if ( geometry.isInstancedBufferGeometry ) { - - var instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); - - renderer.renderInstances( geometry, drawStart, drawCount, instanceCount ); - - } else { - - renderer.render( drawStart, drawCount ); - - } - - }; - - function setupVertexAttributes( object, geometry, material, program ) { - - if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { - - if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { return; } - - } - - state.initAttributes(); - - var geometryAttributes = geometry.attributes; - - var programAttributes = program.getAttributes(); - - var materialDefaultAttributeValues = material.defaultAttributeValues; - - for ( var name in programAttributes ) { - - var programAttribute = programAttributes[ name ]; - - if ( programAttribute >= 0 ) { - - var geometryAttribute = geometryAttributes[ name ]; - - if ( geometryAttribute !== undefined ) { - - var normalized = geometryAttribute.normalized; - var size = geometryAttribute.itemSize; - - var attribute = attributes.get( geometryAttribute ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) { continue; } - - var buffer = attribute.buffer; - var type = attribute.type; - var bytesPerElement = attribute.bytesPerElement; - - if ( geometryAttribute.isInterleavedBufferAttribute ) { - - var data = geometryAttribute.data; - var stride = data.stride; - var offset = geometryAttribute.offset; - - if ( data && data.isInstancedInterleavedBuffer ) { - - state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); - - if ( geometry._maxInstanceCount === undefined ) { - - geometry._maxInstanceCount = data.meshPerAttribute * data.count; - - } - - } else { - - state.enableAttribute( programAttribute ); - - } - - _gl.bindBuffer( 34962, buffer ); - state.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); - - } else { - - if ( geometryAttribute.isInstancedBufferAttribute ) { - - state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); - - if ( geometry._maxInstanceCount === undefined ) { - - geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - - } - - } else { - - state.enableAttribute( programAttribute ); - - } - - _gl.bindBuffer( 34962, buffer ); - state.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); - - } - - } else if ( name === 'instanceMatrix' ) { - - var attribute = attributes.get( object.instanceMatrix ); - - // TODO Attribute may not be available on context restore - - if ( attribute === undefined ) { continue; } - - var buffer = attribute.buffer; - var type = attribute.type; - - state.enableAttributeAndDivisor( programAttribute + 0, 1 ); - state.enableAttributeAndDivisor( programAttribute + 1, 1 ); - state.enableAttributeAndDivisor( programAttribute + 2, 1 ); - state.enableAttributeAndDivisor( programAttribute + 3, 1 ); - - _gl.bindBuffer( 34962, buffer ); - - _gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 ); - _gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 ); - _gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 ); - _gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 ); - - } else if ( materialDefaultAttributeValues !== undefined ) { - - var value = materialDefaultAttributeValues[ name ]; - - if ( value !== undefined ) { - - switch ( value.length ) { - - case 2: - _gl.vertexAttrib2fv( programAttribute, value ); - break; - - case 3: - _gl.vertexAttrib3fv( programAttribute, value ); - break; - - case 4: - _gl.vertexAttrib4fv( programAttribute, value ); - break; - - default: - _gl.vertexAttrib1fv( programAttribute, value ); - - } - - } - - } - - } - - } - - state.disableUnusedAttributes(); - - } - - // Compile - - this.compile = function ( scene, camera ) { - - currentRenderState = renderStates.get( scene, camera ); - currentRenderState.init(); - - scene.traverse( function ( object ) { - - if ( object.isLight ) { - - currentRenderState.pushLight( object ); - - if ( object.castShadow ) { - - currentRenderState.pushShadow( object ); - - } - - } - - } ); - - currentRenderState.setupLights( camera ); - - var compiled = {}; - - scene.traverse( function ( object ) { - - var material = object.material; - - if ( material ) { - - if ( Array.isArray( material ) ) { - - for ( var i = 0; i < material.length; i ++ ) { - - var material2 = material[ i ]; - - if ( material2.uuid in compiled === false ) { - - initMaterial( material2, scene, object ); - compiled[ material2.uuid ] = true; - - } - - } - - } else if ( material.uuid in compiled === false ) { - - initMaterial( material, scene, object ); - compiled[ material.uuid ] = true; - - } - - } - - } ); - - }; - - // Animation Loop - - var onAnimationFrameCallback = null; - - function onAnimationFrame( time ) { - - if ( xr.isPresenting ) { return; } - if ( onAnimationFrameCallback ) { onAnimationFrameCallback( time ); } - - } - - var animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); - - if ( typeof window !== 'undefined' ) { animation.setContext( window ); } - - this.setAnimationLoop = function ( callback ) { - - onAnimationFrameCallback = callback; - xr.setAnimationLoop( callback ); - - animation.start(); - - }; - - // Rendering - - this.render = function ( scene, camera ) { - - var renderTarget, forceClear; - - if ( arguments[ 2 ] !== undefined ) { - - console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' ); - renderTarget = arguments[ 2 ]; - - } - - if ( arguments[ 3 ] !== undefined ) { - - console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' ); - forceClear = arguments[ 3 ]; - - } - - if ( ! ( camera && camera.isCamera ) ) { - - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; - - } - - if ( _isContextLost ) { return; } - - // reset caching for this frame - - _currentGeometryProgram.geometry = null; - _currentGeometryProgram.program = null; - _currentGeometryProgram.wireframe = false; - _currentMaterialId = - 1; - _currentCamera = null; - - // update scene graph - - if ( scene.autoUpdate === true ) { scene.updateMatrixWorld(); } - - // update camera matrices and frustum - - if ( camera.parent === null ) { camera.updateMatrixWorld(); } - - if ( xr.enabled && xr.isPresenting ) { - - camera = xr.getCamera( camera ); - - } - - // - scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); - - currentRenderState = renderStates.get( scene, camera ); - currentRenderState.init(); - - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromProjectionMatrix( _projScreenMatrix ); - - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); - - currentRenderList = renderLists.get( scene, camera ); - currentRenderList.init(); - - projectObject( scene, camera, 0, _this.sortObjects ); - - currentRenderList.finish(); - - if ( _this.sortObjects === true ) { - - currentRenderList.sort( _opaqueSort, _transparentSort ); - - } - - // - - if ( _clippingEnabled ) { _clipping.beginShadows(); } - - var shadowsArray = currentRenderState.state.shadowsArray; - - shadowMap.render( shadowsArray, scene, camera ); - - currentRenderState.setupLights( camera ); - - if ( _clippingEnabled ) { _clipping.endShadows(); } - - // - - if ( this.info.autoReset ) { this.info.reset(); } - - if ( renderTarget !== undefined ) { - - this.setRenderTarget( renderTarget ); - - } - - // - - background.render( currentRenderList, scene, camera, forceClear ); - - // render scene - - var opaqueObjects = currentRenderList.opaque; - var transparentObjects = currentRenderList.transparent; - - if ( scene.overrideMaterial ) { - - var overrideMaterial = scene.overrideMaterial; - - if ( opaqueObjects.length ) { renderObjects( opaqueObjects, scene, camera, overrideMaterial ); } - if ( transparentObjects.length ) { renderObjects( transparentObjects, scene, camera, overrideMaterial ); } - - } else { - - // opaque pass (front-to-back order) - - if ( opaqueObjects.length ) { renderObjects( opaqueObjects, scene, camera ); } - - // transparent pass (back-to-front order) - - if ( transparentObjects.length ) { renderObjects( transparentObjects, scene, camera ); } - - } - - // - - scene.onAfterRender( _this, scene, camera ); - - // - - if ( _currentRenderTarget !== null ) { - - // Generate mipmap if we're using any kind of mipmap filtering - - textures.updateRenderTargetMipmap( _currentRenderTarget ); - - // resolve multisample renderbuffers to a single-sample texture if necessary - - textures.updateMultisampleRenderTarget( _currentRenderTarget ); - - } - - // Ensure depth buffer writing is enabled so it can be cleared on next render - - state.buffers.depth.setTest( true ); - state.buffers.depth.setMask( true ); - state.buffers.color.setMask( true ); - - state.setPolygonOffset( false ); - - // _gl.finish(); - - currentRenderList = null; - currentRenderState = null; - - }; - - function projectObject( object, camera, groupOrder, sortObjects ) { - - if ( object.visible === false ) { return; } - - var visible = object.layers.test( camera.layers ); - - if ( visible ) { - - if ( object.isGroup ) { - - groupOrder = object.renderOrder; - - } else if ( object.isLOD ) { - - if ( object.autoUpdate === true ) { object.update( camera ); } - - } else if ( object.isLight ) { - - currentRenderState.pushLight( object ); - - if ( object.castShadow ) { - - currentRenderState.pushShadow( object ); - - } - - } else if ( object.isSprite ) { - - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - var geometry = objects.update( object ); - var material = object.material; - - if ( material.visible ) { - - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } else if ( object.isImmediateRenderObject ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); - - } else if ( object.isMesh || object.isLine || object.isPoints ) { - - if ( object.isSkinnedMesh ) { - - // update skeleton only once in a frame - - if ( object.skeleton.frame !== info.render.frame ) { - - object.skeleton.update(); - object.skeleton.frame = info.render.frame; - - } - - } - - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - - if ( sortObjects ) { - - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); - - } - - var geometry = objects.update( object ); - var material = object.material; - - if ( Array.isArray( material ) ) { - - var groups = geometry.groups; - - for ( var i = 0, l = groups.length; i < l; i ++ ) { - - var group = groups[ i ]; - var groupMaterial = material[ group.materialIndex ]; - - if ( groupMaterial && groupMaterial.visible ) { - - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - - } - - } - - } else if ( material.visible ) { - - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - - } - - } - - } - - } - - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - projectObject( children[ i ], camera, groupOrder, sortObjects ); - - } - - } - - function renderObjects( renderList, scene, camera, overrideMaterial ) { - - for ( var i = 0, l = renderList.length; i < l; i ++ ) { - - var renderItem = renderList[ i ]; - - var object = renderItem.object; - var geometry = renderItem.geometry; - var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; - var group = renderItem.group; - - if ( camera.isArrayCamera ) { - - _currentArrayCamera = camera; - - var cameras = camera.cameras; - - for ( var j = 0, jl = cameras.length; j < jl; j ++ ) { - - var camera2 = cameras[ j ]; - - if ( object.layers.test( camera2.layers ) ) { - - state.viewport( _currentViewport.copy( camera2.viewport ) ); - - currentRenderState.setupLights( camera2 ); - - renderObject( object, scene, camera2, geometry, material, group ); - - } - - } - - } else { - - _currentArrayCamera = null; - - renderObject( object, scene, camera, geometry, material, group ); - - } - - } - - } - - function renderObject( object, scene, camera, geometry, material, group ) { - - object.onBeforeRender( _this, scene, camera, geometry, material, group ); - currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); - - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - - if ( object.isImmediateRenderObject ) { - - var program = setProgram( camera, scene, material, object ); - - state.setMaterial( material ); - - _currentGeometryProgram.geometry = null; - _currentGeometryProgram.program = null; - _currentGeometryProgram.wireframe = false; - - renderObjectImmediate( object, program ); - - } else { - - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - - } - - object.onAfterRender( _this, scene, camera, geometry, material, group ); - currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); - - } - - function initMaterial( material, scene, object ) { - - var materialProperties = properties.get( material ); - - var lights = currentRenderState.state.lights; - var shadowsArray = currentRenderState.state.shadowsArray; - - var lightsStateVersion = lights.state.version; - - var parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, _clipping.numPlanes, _clipping.numIntersection, object ); - var programCacheKey = programCache.getProgramCacheKey( parameters ); - - var program = materialProperties.program; - var programChange = true; - - if ( program === undefined ) { - - // new material - material.addEventListener( 'dispose', onMaterialDispose ); - - } else if ( program.cacheKey !== programCacheKey ) { - - // changed glsl or parameters - releaseMaterialProgramReference( material ); - - } else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) { - - materialProperties.lightsStateVersion = lightsStateVersion; - - programChange = false; - - } else if ( parameters.shaderID !== undefined ) { - - // same glsl and uniform list - return; - - } else { - - // only rebuild uniform list - programChange = false; - - } - - if ( programChange ) { - - program = programCache.acquireProgram( parameters, programCacheKey ); - - materialProperties.program = program; - materialProperties.uniforms = parameters.uniforms; - materialProperties.outputEncoding = parameters.outputEncoding; - material.program = program; - - } - - var programAttributes = program.getAttributes(); - - if ( material.morphTargets ) { - - material.numSupportedMorphTargets = 0; - - for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { - - if ( programAttributes[ 'morphTarget' + i ] >= 0 ) { - - material.numSupportedMorphTargets ++; - - } - - } - - } - - if ( material.morphNormals ) { - - material.numSupportedMorphNormals = 0; - - for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { - - if ( programAttributes[ 'morphNormal' + i ] >= 0 ) { - - material.numSupportedMorphNormals ++; - - } - - } - - } - - var uniforms = materialProperties.uniforms; - - if ( ! material.isShaderMaterial && - ! material.isRawShaderMaterial || - material.clipping === true ) { - - materialProperties.numClippingPlanes = _clipping.numPlanes; - materialProperties.numIntersection = _clipping.numIntersection; - uniforms.clippingPlanes = _clipping.uniform; - - } - - materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.fog = scene.fog; - - // store the light setup it was created for - - materialProperties.needsLights = materialNeedsLights( material ); - materialProperties.lightsStateVersion = lightsStateVersion; - - if ( materialProperties.needsLights ) { - - // wire up the material to this renderer's lighting state - - uniforms.ambientLightColor.value = lights.state.ambient; - uniforms.lightProbe.value = lights.state.probe; - uniforms.directionalLights.value = lights.state.directional; - uniforms.directionalLightShadows.value = lights.state.directionalShadow; - uniforms.spotLights.value = lights.state.spot; - uniforms.spotLightShadows.value = lights.state.spotShadow; - uniforms.rectAreaLights.value = lights.state.rectArea; - uniforms.pointLights.value = lights.state.point; - uniforms.pointLightShadows.value = lights.state.pointShadow; - uniforms.hemisphereLights.value = lights.state.hemi; - - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; - uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; - uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; - uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms - - } - - var progUniforms = materialProperties.program.getUniforms(), - uniformsList = - WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - - materialProperties.uniformsList = uniformsList; - - } - - function setProgram( camera, scene, material, object ) { - - textures.resetTextureUnits(); - - var fog = scene.fog; - var environment = material.isMeshStandardMaterial ? scene.environment : null; - var encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding; - - var materialProperties = properties.get( material ); - var lights = currentRenderState.state.lights; - - if ( _clippingEnabled ) { - - if ( _localClippingEnabled || camera !== _currentCamera ) { - - var useCache = - camera === _currentCamera && - material.id === _currentMaterialId; - - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - _clipping.setState( - material.clippingPlanes, material.clipIntersection, material.clipShadows, - camera, materialProperties, useCache ); - - } - - } - - if ( material.version === materialProperties.__version ) { - - if ( materialProperties.program === undefined ) { - - initMaterial( material, scene, object ); - - } else if ( material.fog && materialProperties.fog !== fog ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.environment !== environment ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== _clipping.numPlanes || - materialProperties.numIntersection !== _clipping.numIntersection ) ) { - - initMaterial( material, scene, object ); - - } else if ( materialProperties.outputEncoding !== encoding ) { - - initMaterial( material, scene, object ); - - } - - } else { - - initMaterial( material, scene, object ); - materialProperties.__version = material.version; - - } - - var refreshProgram = false; - var refreshMaterial = false; - var refreshLights = false; - - var program = materialProperties.program, - p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.uniforms; - - if ( state.useProgram( program.program ) ) { - - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; - - } - - if ( material.id !== _currentMaterialId ) { - - _currentMaterialId = material.id; - - refreshMaterial = true; - - } - - if ( refreshProgram || _currentCamera !== camera ) { - - p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); - - if ( capabilities.logarithmicDepthBuffer ) { - - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - - } - - if ( _currentCamera !== camera ) { - - _currentCamera = camera; - - // lighting uniforms depend on the camera so enforce an update - // now, in case this material supports lights - or later, when - // the next material that does gets activated: - - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done - - } - - // load material specific uniforms - // (shader material also gets them for the sake of genericity) - - if ( material.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshStandardMaterial || - material.envMap ) { - - var uCamPos = p_uniforms.map.cameraPosition; - - if ( uCamPos !== undefined ) { - - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - - } - - } - - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial ) { - - p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); - - } - - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.skinning ) { - - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - - } - - } - - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // otherwise textures used for skinning can take over texture units reserved for other material textures - - if ( material.skinning ) { - - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - - var skeleton = object.skeleton; - - if ( skeleton ) { - - var bones = skeleton.bones; - - if ( capabilities.floatVertexTextures ) { - - if ( skeleton.boneTexture === undefined ) { - - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - - - var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix - size = MathUtils.ceilPowerOfTwo( size ); - size = Math.max( size, 4 ); - - var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel - boneMatrices.set( skeleton.boneMatrices ); // copy current values - - var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); - - skeleton.boneMatrices = boneMatrices; - skeleton.boneTexture = boneTexture; - skeleton.boneTextureSize = size; - - } - - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); - p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); - - } else { - - p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); - - } - - } - - } - - if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { - - materialProperties.receiveShadow = object.receiveShadow; - p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); - - } - - if ( refreshMaterial ) { - - p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint ); - - if ( materialProperties.needsLights ) { - - // the current material requires lighting info - - // note: all lighting uniforms are always set correctly - // they simply reference the renderer's state for their - // values - // - // use the current material's .needsUpdate flags to set - // the GL state when required - - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); - - } - - // refresh uniforms common to several materials - - if ( fog && material.fog ) { - - materials.refreshFogUniforms( m_uniforms, fog ); - - } - - materials.refreshMaterialUniforms( m_uniforms, material, environment, _pixelRatio, _height ); - - // RectAreaLight Texture - // TODO (mrdoob): Find a nicer implementation - - if ( m_uniforms.ltc_1 !== undefined ) { m_uniforms.ltc_1.value = UniformsLib.LTC_1; } - if ( m_uniforms.ltc_2 !== undefined ) { m_uniforms.ltc_2.value = UniformsLib.LTC_2; } - - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - - } - - if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - material.uniformsNeedUpdate = false; - - } - - if ( material.isSpriteMaterial ) { - - p_uniforms.setValue( _gl, 'center', object.center ); - - } - - // common matrices - - p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); - p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - - return program; - - } - - // If uniforms are marked as clean, they don't need to be loaded to the GPU. - - function markUniformsLightsNeedsUpdate( uniforms, value ) { - - uniforms.ambientLightColor.needsUpdate = value; - uniforms.lightProbe.needsUpdate = value; - - uniforms.directionalLights.needsUpdate = value; - uniforms.directionalLightShadows.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.pointLightShadows.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.spotLightShadows.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; - - } - - function materialNeedsLights( material ) { - - return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || - material.isMeshStandardMaterial || material.isShadowMaterial || - ( material.isShaderMaterial && material.lights === true ); - - } - - // - this.setFramebuffer = function ( value ) { - - if ( _framebuffer !== value && _currentRenderTarget === null ) { _gl.bindFramebuffer( 36160, value ); } - - _framebuffer = value; - - }; - - this.getActiveCubeFace = function () { - - return _currentActiveCubeFace; - - }; - - this.getActiveMipmapLevel = function () { - - return _currentActiveMipmapLevel; - - }; - - this.getRenderTarget = function () { - - return _currentRenderTarget; - - }; - - this.setRenderTarget = function ( renderTarget, activeCubeFace, activeMipmapLevel ) { - - _currentRenderTarget = renderTarget; - _currentActiveCubeFace = activeCubeFace; - _currentActiveMipmapLevel = activeMipmapLevel; - - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { - - textures.setupRenderTarget( renderTarget ); - - } - - var framebuffer = _framebuffer; - var isCube = false; - - if ( renderTarget ) { - - var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; - - if ( renderTarget.isWebGLCubeRenderTarget ) { - - framebuffer = __webglFramebuffer[ activeCubeFace || 0 ]; - isCube = true; - - } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { - - framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; - - } else { - - framebuffer = __webglFramebuffer; - - } - - _currentViewport.copy( renderTarget.viewport ); - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; - - } else { - - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); - _currentScissorTest = _scissorTest; - - } - - if ( _currentFramebuffer !== framebuffer ) { - - _gl.bindFramebuffer( 36160, framebuffer ); - _currentFramebuffer = framebuffer; - - } - - state.viewport( _currentViewport ); - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); - - if ( isCube ) { - - var textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( 36160, 36064, 34069 + ( activeCubeFace || 0 ), textureProperties.__webglTexture, activeMipmapLevel || 0 ); - - } - - }; - - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { - - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; - - } - - var framebuffer = properties.get( renderTarget ).__webglFramebuffer; - - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { - - framebuffer = framebuffer[ activeCubeFaceIndex ]; - - } - - if ( framebuffer ) { - - var restore = false; - - if ( framebuffer !== _currentFramebuffer ) { - - _gl.bindFramebuffer( 36160, framebuffer ); - - restore = true; - - } - - try { - - var texture = renderTarget.texture; - var textureFormat = texture.format; - var textureType = texture.type; - - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; - - } - - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // IE11, Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! ( textureType === HalfFloatType && ( capabilities.isWebGL2 ? extensions.get( 'EXT_color_buffer_float' ) : extensions.get( 'EXT_color_buffer_half_float' ) ) ) ) { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; - - } - - if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { - - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); - - } - - } else { - - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); - - } - - } finally { - - if ( restore ) { - - _gl.bindFramebuffer( 36160, _currentFramebuffer ); - - } - - } - - } - - }; - - this.copyFramebufferToTexture = function ( position, texture, level ) { - - if ( level === undefined ) { level = 0; } - - var levelScale = Math.pow( 2, - level ); - var width = Math.floor( texture.image.width * levelScale ); - var height = Math.floor( texture.image.height * levelScale ); - var glFormat = utils.convert( texture.format ); - - textures.setTexture2D( texture, 0 ); - - _gl.copyTexImage2D( 3553, level, glFormat, position.x, position.y, width, height, 0 ); - - state.unbindTexture(); - - }; - - this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) { - - if ( level === undefined ) { level = 0; } - - var width = srcTexture.image.width; - var height = srcTexture.image.height; - var glFormat = utils.convert( dstTexture.format ); - var glType = utils.convert( dstTexture.type ); - - textures.setTexture2D( dstTexture, 0 ); - - if ( srcTexture.isDataTexture ) { - - _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - - } else { - - if ( srcTexture.isCompressedTexture ) { - - _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); - - } else { - - _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); - - } - - } - - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) { _gl.generateMipmap( 3553 ); } - - state.unbindTexture(); - - }; - - this.initTexture = function ( texture ) { - - textures.setTexture2D( texture, 0 ); - - state.unbindTexture(); - - }; - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef - - } - - } - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function FogExp2( color, density ) { - - this.name = ''; - - this.color = new Color( color ); - this.density = ( density !== undefined ) ? density : 0.00025; - - } - - Object.assign( FogExp2.prototype, { - - isFogExp2: true, - - clone: function () { - - return new FogExp2( this.color, this.density ); - - }, - - toJSON: function ( /* meta */ ) { - - return { - type: 'FogExp2', - color: this.color.getHex(), - density: this.density - }; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function Fog( color, near, far ) { - - this.name = ''; - - this.color = new Color( color ); - - this.near = ( near !== undefined ) ? near : 1; - this.far = ( far !== undefined ) ? far : 1000; - - } - - Object.assign( Fog.prototype, { - - isFog: true, - - clone: function () { - - return new Fog( this.color, this.near, this.far ); - - }, - - toJSON: function ( /* meta */ ) { - - return { - type: 'Fog', - color: this.color.getHex(), - near: this.near, - far: this.far - }; - - } - - } ); - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InterleavedBuffer( array, stride ) { - - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; - - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - } - - Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) { this.version ++; } - - } - - } ); - - Object.assign( InterleavedBuffer.prototype, { - - isInterleavedBuffer: true, - - onUploadCallback: function () {}, - - setUsage: function ( value ) { - - this.usage = value; - - return this; - - }, - - copy: function ( source ) { - - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.usage = source.usage; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.stride; - index2 *= attribute.stride; - - for ( var i = 0, l = this.stride; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - set: function ( value, offset ) { - - if ( offset === undefined ) { offset = 0; } - - this.array.set( value, offset ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - onUpload: function ( callback ) { - - this.onUploadCallback = callback; - - return this; - - } - - } ); - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - var _vector$6 = new Vector3(); - - function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { - - this.name = ''; - - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; - - this.normalized = normalized === true; - - } - - Object.defineProperties( InterleavedBufferAttribute.prototype, { - - count: { - - get: function () { - - return this.data.count; - - } - - }, - - array: { - - get: function () { - - return this.data.array; - - } - - } - - } ); - - Object.assign( InterleavedBufferAttribute.prototype, { - - isInterleavedBufferAttribute: true, - - applyMatrix4: function ( m ) { - - for ( var i = 0, l = this.data.count; i < l; i ++ ) { - - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); - - _vector$6.applyMatrix4( m ); - - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - - } - - return this; - - }, - - setX: function ( index, x ) { - - this.data.array[ index * this.data.stride + this.offset ] = x; - - return this; - - }, - - setY: function ( index, y ) { - - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; - - return this; - - }, - - setZ: function ( index, z ) { - - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; - - return this; - - }, - - setW: function ( index, w ) { - - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - - return this; - - }, - - getX: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset ]; - - }, - - getY: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 1 ]; - - }, - - getZ: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 2 ]; - - }, - - getW: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 3 ]; - - }, - - setXY: function ( index, x, y ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - - return this; - - }, - - setXYZ: function ( index, x, y, z ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - - return this; - - }, - - setXYZW: function ( index, x, y, z, w ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - this.data.array[ index + 3 ] = w; - - return this; - - }, - - clone: function () { - - console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); - - var array = []; - - for ( var i = 0; i < this.count; i ++ ) { - - var index = i * this.data.stride + this.offset; - - for ( var j = 0; j < this.itemSize; j ++ ) { - - array.push( this.data.array[ index + j ] ); - - } - - } - - return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); - - }, - - toJSON: function () { - - console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); - - var array = []; - - for ( var i = 0; i < this.count; i ++ ) { - - var index = i * this.data.stride + this.offset; - - for ( var j = 0; j < this.itemSize; j ++ ) { - - array.push( this.data.array[ index + j ] ); - - } - - } - - // deinterleave data and save it as an ordinary buffer attribute for now - - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: array, - normalized: this.normalized - }; - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * rotation: , - * sizeAttenuation: - * } - */ - - function SpriteMaterial( parameters ) { - - Material.call( this ); - - this.type = 'SpriteMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - - this.alphaMap = null; - - this.rotation = 0; - - this.sizeAttenuation = true; - - this.transparent = true; - - this.setValues( parameters ); - - } - - SpriteMaterial.prototype = Object.create( Material.prototype ); - SpriteMaterial.prototype.constructor = SpriteMaterial; - SpriteMaterial.prototype.isSpriteMaterial = true; - - SpriteMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.rotation = source.rotation; - - this.sizeAttenuation = source.sizeAttenuation; - - return this; - - }; - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ - - var _geometry; - - var _intersectPoint = new Vector3(); - var _worldScale = new Vector3(); - var _mvPosition = new Vector3(); - - var _alignedPosition = new Vector2(); - var _rotatedPosition = new Vector2(); - var _viewWorldMatrix = new Matrix4(); - - var _vA$1 = new Vector3(); - var _vB$1 = new Vector3(); - var _vC$1 = new Vector3(); - - var _uvA$1 = new Vector2(); - var _uvB$1 = new Vector2(); - var _uvC$1 = new Vector2(); - - function Sprite( material ) { - - Object3D.call( this ); - - this.type = 'Sprite'; - - if ( _geometry === undefined ) { - - _geometry = new BufferGeometry(); - - var float32Array = new Float32Array( [ - - 0.5, - 0.5, 0, 0, 0, - 0.5, - 0.5, 0, 1, 0, - 0.5, 0.5, 0, 1, 1, - - 0.5, 0.5, 0, 0, 1 - ] ); - - var interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); - - _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); - _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); - _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); - - } - - this.geometry = _geometry; - this.material = ( material !== undefined ) ? material : new SpriteMaterial(); - - this.center = new Vector2( 0.5, 0.5 ); - - } - - Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Sprite, - - isSprite: true, - - raycast: function ( raycaster, intersects ) { - - if ( raycaster.camera === null ) { - - console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); - - } - - _worldScale.setFromMatrixScale( this.matrixWorld ); - - _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); - this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); - - _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); - - if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { - - _worldScale.multiplyScalar( - _mvPosition.z ); - - } - - var rotation = this.material.rotation; - var sin, cos; - if ( rotation !== 0 ) { - - cos = Math.cos( rotation ); - sin = Math.sin( rotation ); - - } - - var center = this.center; - - transformVertex( _vA$1.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vB$1.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vC$1.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - - _uvA$1.set( 0, 0 ); - _uvB$1.set( 1, 0 ); - _uvC$1.set( 1, 1 ); - - // check first triangle - var intersect = raycaster.ray.intersectTriangle( _vA$1, _vB$1, _vC$1, false, _intersectPoint ); - - if ( intersect === null ) { - - // check second triangle - transformVertex( _vB$1.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - _uvB$1.set( 0, 1 ); - - intersect = raycaster.ray.intersectTriangle( _vA$1, _vC$1, _vB$1, false, _intersectPoint ); - if ( intersect === null ) { - - return; - - } - - } - - var distance = raycaster.ray.origin.distanceTo( _intersectPoint ); - - if ( distance < raycaster.near || distance > raycaster.far ) { return; } - - intersects.push( { - - distance: distance, - point: _intersectPoint.clone(), - uv: Triangle.getUV( _intersectPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ), - face: null, - object: this - - } ); - - }, - - clone: function () { - - return new this.constructor( this.material ).copy( this ); - - }, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - if ( source.center !== undefined ) { this.center.copy( source.center ); } - - return this; - - } - - - } ); - - function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { - - // compute position in camera space - _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); - - // to check if rotation is not zero - if ( sin !== undefined ) { - - _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); - _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); - - } else { - - _rotatedPosition.copy( _alignedPosition ); - - } - - - vertexPosition.copy( mvPosition ); - vertexPosition.x += _rotatedPosition.x; - vertexPosition.y += _rotatedPosition.y; - - // transform to world space - vertexPosition.applyMatrix4( _viewWorldMatrix ); - - } - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - - var _v1$4 = new Vector3(); - var _v2$2 = new Vector3(); - - function LOD() { - - Object3D.call( this ); - - this._currentLevel = 0; - - this.type = 'LOD'; - - Object.defineProperties( this, { - levels: { - enumerable: true, - value: [] - } - } ); - - this.autoUpdate = true; - - } - - LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: LOD, - - isLOD: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source, false ); - - var levels = source.levels; - - for ( var i = 0, l = levels.length; i < l; i ++ ) { - - var level = levels[ i ]; - - this.addLevel( level.object.clone(), level.distance ); - - } - - this.autoUpdate = source.autoUpdate; - - return this; - - }, - - addLevel: function ( object, distance ) { - - if ( distance === undefined ) { distance = 0; } - - distance = Math.abs( distance ); - - var levels = this.levels; - - for ( var l = 0; l < levels.length; l ++ ) { - - if ( distance < levels[ l ].distance ) { - - break; - - } - - } - - levels.splice( l, 0, { distance: distance, object: object } ); - - this.add( object ); - - return this; - - }, - - getCurrentLevel: function () { - - return this._currentLevel; - - }, - - getObjectForDistance: function ( distance ) { - - var levels = this.levels; - - if ( levels.length > 0 ) { - - for ( var i = 1, l = levels.length; i < l; i ++ ) { - - if ( distance < levels[ i ].distance ) { - - break; - - } - - } - - return levels[ i - 1 ].object; - - } - - return null; - - }, - - raycast: function ( raycaster, intersects ) { - - var levels = this.levels; - - if ( levels.length > 0 ) { - - _v1$4.setFromMatrixPosition( this.matrixWorld ); - - var distance = raycaster.ray.origin.distanceTo( _v1$4 ); - - this.getObjectForDistance( distance ).raycast( raycaster, intersects ); - - } - - }, - - update: function ( camera ) { - - var levels = this.levels; - - if ( levels.length > 1 ) { - - _v1$4.setFromMatrixPosition( camera.matrixWorld ); - _v2$2.setFromMatrixPosition( this.matrixWorld ); - - var distance = _v1$4.distanceTo( _v2$2 ) / camera.zoom; - - levels[ 0 ].object.visible = true; - - for ( var i = 1, l = levels.length; i < l; i ++ ) { - - if ( distance >= levels[ i ].distance ) { - - levels[ i - 1 ].object.visible = false; - levels[ i ].object.visible = true; - - } else { - - break; - - } - - } - - this._currentLevel = i - 1; - - for ( ; i < l; i ++ ) { - - levels[ i ].object.visible = false; - - } - - } - - }, - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - if ( this.autoUpdate === false ) { data.object.autoUpdate = false; } - - data.object.levels = []; - - var levels = this.levels; - - for ( var i = 0, l = levels.length; i < l; i ++ ) { - - var level = levels[ i ]; - - data.object.levels.push( { - object: level.object.uuid, - distance: level.distance - } ); - - } - - return data; - - } - - } ); - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com - */ - - function SkinnedMesh( geometry, material ) { - - if ( geometry && geometry.isGeometry ) { - - console.error( 'THREE.SkinnedMesh no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - Mesh.call( this, geometry, material ); - - this.type = 'SkinnedMesh'; - - this.bindMode = 'attached'; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); - - } - - SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: SkinnedMesh, - - isSkinnedMesh: true, - - bind: function ( skeleton, bindMatrix ) { - - this.skeleton = skeleton; - - if ( bindMatrix === undefined ) { - - this.updateMatrixWorld( true ); - - this.skeleton.calculateInverses(); - - bindMatrix = this.matrixWorld; - - } - - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.getInverse( bindMatrix ); - - }, - - pose: function () { - - this.skeleton.pose(); - - }, - - normalizeSkinWeights: function () { - - var vector = new Vector4(); - - var skinWeight = this.geometry.attributes.skinWeight; - - for ( var i = 0, l = skinWeight.count; i < l; i ++ ) { - - vector.x = skinWeight.getX( i ); - vector.y = skinWeight.getY( i ); - vector.z = skinWeight.getZ( i ); - vector.w = skinWeight.getW( i ); - - var scale = 1.0 / vector.manhattanLength(); - - if ( scale !== Infinity ) { - - vector.multiplyScalar( scale ); - - } else { - - vector.set( 1, 0, 0, 0 ); // do something reasonable - - } - - skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); - - } - - }, - - updateMatrixWorld: function ( force ) { - - Mesh.prototype.updateMatrixWorld.call( this, force ); - - if ( this.bindMode === 'attached' ) { - - this.bindMatrixInverse.getInverse( this.matrixWorld ); - - } else if ( this.bindMode === 'detached' ) { - - this.bindMatrixInverse.getInverse( this.bindMatrix ); - - } else { - - console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); - - } - - }, - - clone: function () { - - return new this.constructor( this.geometry, this.material ).copy( this ); - - }, - - boneTransform: ( function () { - - var basePosition = new Vector3(); - - var skinIndex = new Vector4(); - var skinWeight = new Vector4(); - - var vector = new Vector3(); - var matrix = new Matrix4(); - - return function ( index, target ) { - - var skeleton = this.skeleton; - var geometry = this.geometry; - - skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); - skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); - - basePosition.fromBufferAttribute( geometry.attributes.position, index ).applyMatrix4( this.bindMatrix ); - - target.set( 0, 0, 0 ); - - for ( var i = 0; i < 4; i ++ ) { - - var weight = skinWeight.getComponent( i ); - - if ( weight !== 0 ) { - - var boneIndex = skinIndex.getComponent( i ); - - matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); - - target.addScaledVector( vector.copy( basePosition ).applyMatrix4( matrix ), weight ); - - } - - } - - return target.applyMatrix4( this.bindMatrixInverse ); - - }; - - }() ) - - } ); - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author michael guerrero / http://realitymeltdown.com - * @author ikerr / http://verold.com - */ - - var _offsetMatrix = new Matrix4(); - var _identityMatrix = new Matrix4(); - - function Skeleton( bones, boneInverses ) { - - // copy the bone array - - bones = bones || []; - - this.bones = bones.slice( 0 ); - this.boneMatrices = new Float32Array( this.bones.length * 16 ); - - this.frame = - 1; - - // use the supplied bone inverses or calculate the inverses - - if ( boneInverses === undefined ) { - - this.calculateInverses(); - - } else { - - if ( this.bones.length === boneInverses.length ) { - - this.boneInverses = boneInverses.slice( 0 ); - - } else { - - console.warn( 'THREE.Skeleton boneInverses is the wrong length.' ); - - this.boneInverses = []; - - for ( var i = 0, il = this.bones.length; i < il; i ++ ) { - - this.boneInverses.push( new Matrix4() ); - - } - - } - - } - - } - - Object.assign( Skeleton.prototype, { - - calculateInverses: function () { - - this.boneInverses = []; - - for ( var i = 0, il = this.bones.length; i < il; i ++ ) { - - var inverse = new Matrix4(); - - if ( this.bones[ i ] ) { - - inverse.getInverse( this.bones[ i ].matrixWorld ); - - } - - this.boneInverses.push( inverse ); - - } - - }, - - pose: function () { - - var bone, i, il; - - // recover the bind-time world matrices - - for ( i = 0, il = this.bones.length; i < il; i ++ ) { - - bone = this.bones[ i ]; - - if ( bone ) { - - bone.matrixWorld.getInverse( this.boneInverses[ i ] ); - - } - - } - - // compute the local matrices, positions, rotations and scales - - for ( i = 0, il = this.bones.length; i < il; i ++ ) { - - bone = this.bones[ i ]; - - if ( bone ) { - - if ( bone.parent && bone.parent.isBone ) { - - bone.matrix.getInverse( bone.parent.matrixWorld ); - bone.matrix.multiply( bone.matrixWorld ); - - } else { - - bone.matrix.copy( bone.matrixWorld ); - - } - - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); - - } - - } - - }, - - update: function () { - - var bones = this.bones; - var boneInverses = this.boneInverses; - var boneMatrices = this.boneMatrices; - var boneTexture = this.boneTexture; - - // flatten bone matrices to array - - for ( var i = 0, il = bones.length; i < il; i ++ ) { - - // compute the offset between the current and the original transform - - var matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; - - _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); - _offsetMatrix.toArray( boneMatrices, i * 16 ); - - } - - if ( boneTexture !== undefined ) { - - boneTexture.needsUpdate = true; - - } - - }, - - clone: function () { - - return new Skeleton( this.bones, this.boneInverses ); - - }, - - getBoneByName: function ( name ) { - - for ( var i = 0, il = this.bones.length; i < il; i ++ ) { - - var bone = this.bones[ i ]; - - if ( bone.name === name ) { - - return bone; - - } - - } - - return undefined; - - }, - - dispose: function ( ) { - - if ( this.boneTexture ) { - - this.boneTexture.dispose(); - - this.boneTexture = undefined; - - } - - } - - } ); - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com - */ - - function Bone() { - - Object3D.call( this ); - - this.type = 'Bone'; - - } - - Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Bone, - - isBone: true - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var _instanceLocalMatrix = new Matrix4(); - var _instanceWorldMatrix = new Matrix4(); - - var _instanceIntersects = []; - - var _mesh = new Mesh(); - - function InstancedMesh( geometry, material, count ) { - - Mesh.call( this, geometry, material ); - - this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 ); - - this.count = count; - - this.frustumCulled = false; - - } - - InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { - - constructor: InstancedMesh, - - isInstancedMesh: true, - - getMatrixAt: function ( index, matrix ) { - - matrix.fromArray( this.instanceMatrix.array, index * 16 ); - - }, - - raycast: function ( raycaster, intersects ) { - - var matrixWorld = this.matrixWorld; - var raycastTimes = this.count; - - _mesh.geometry = this.geometry; - _mesh.material = this.material; - - if ( _mesh.material === undefined ) { return; } - - for ( var instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { - - // calculate the world matrix for each instance - - this.getMatrixAt( instanceId, _instanceLocalMatrix ); - - _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); - - // the mesh represents this single instance - - _mesh.matrixWorld = _instanceWorldMatrix; - - _mesh.raycast( raycaster, _instanceIntersects ); - - // process the result of raycast - - for ( var i = 0, l = _instanceIntersects.length; i < l; i ++ ) { - - var intersect = _instanceIntersects[ i ]; - intersect.instanceId = instanceId; - intersect.object = this; - intersects.push( intersect ); - - } - - _instanceIntersects.length = 0; - - } - - }, - - setMatrixAt: function ( index, matrix ) { - - matrix.toArray( this.instanceMatrix.array, index * 16 ); - - }, - - updateMorphTargets: function () { - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ - - function LineBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'LineBasicMaterial'; - - this.color = new Color( 0xffffff ); - - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; - - this.morphTargets = false; - - this.setValues( parameters ); - - } - - LineBasicMaterial.prototype = Object.create( Material.prototype ); - LineBasicMaterial.prototype.constructor = LineBasicMaterial; - - LineBasicMaterial.prototype.isLineBasicMaterial = true; - - LineBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; - - this.morphTargets = source.morphTargets; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var _start = new Vector3(); - var _end = new Vector3(); - var _inverseMatrix$1 = new Matrix4(); - var _ray$1 = new Ray(); - var _sphere$2 = new Sphere(); - - function Line( geometry, material, mode ) { - - if ( mode === 1 ) { - - console.error( 'THREE.Line: parameter THREE.LinePieces no longer supported. Use THREE.LineSegments instead.' ); - - } - - Object3D.call( this ); - - this.type = 'Line'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new LineBasicMaterial(); - - this.updateMorphTargets(); - - } - - Line.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Line, - - isLine: true, - - computeLineDistances: function () { - - var geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - // we assume non-indexed geometry - - if ( geometry.index === null ) { - - var positionAttribute = geometry.attributes.position; - var lineDistances = [ 0 ]; - - for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) { - - _start.fromBufferAttribute( positionAttribute, i - 1 ); - _end.fromBufferAttribute( positionAttribute, i ); - - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += _start.distanceTo( _end ); - - } - - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - - } else { - - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - - } - - } else if ( geometry.isGeometry ) { - - var vertices = geometry.vertices; - var lineDistances = geometry.lineDistances; - - lineDistances[ 0 ] = 0; - - for ( var i = 1, l = vertices.length; i < l; i ++ ) { - - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] ); - - } - - } - - return this; - - }, - - raycast: function ( raycaster, intersects ) { - - var geometry = this.geometry; - var matrixWorld = this.matrixWorld; - var threshold = raycaster.params.Line.threshold; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) { geometry.computeBoundingSphere(); } - - _sphere$2.copy( geometry.boundingSphere ); - _sphere$2.applyMatrix4( matrixWorld ); - _sphere$2.radius += threshold; - - if ( raycaster.ray.intersectsSphere( _sphere$2 ) === false ) { return; } - - // - - _inverseMatrix$1.getInverse( matrixWorld ); - _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); - - var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - var localThresholdSq = localThreshold * localThreshold; - - var vStart = new Vector3(); - var vEnd = new Vector3(); - var interSegment = new Vector3(); - var interRay = new Vector3(); - var step = ( this && this.isLineSegments ) ? 2 : 1; - - if ( geometry.isBufferGeometry ) { - - var index = geometry.index; - var attributes = geometry.attributes; - var positions = attributes.position.array; - - if ( index !== null ) { - - var indices = index.array; - - for ( var i = 0, l = indices.length - 1; i < l; i += step ) { - - var a = indices[ i ]; - var b = indices[ i + 1 ]; - - vStart.fromArray( positions, a * 3 ); - vEnd.fromArray( positions, b * 3 ); - - var distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > localThresholdSq ) { continue; } - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - var distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) { continue; } - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } else { - - for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { - - vStart.fromArray( positions, 3 * i ); - vEnd.fromArray( positions, 3 * i + 3 ); - - var distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - - if ( distSq > localThresholdSq ) { continue; } - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - var distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) { continue; } - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } - - } else if ( geometry.isGeometry ) { - - var vertices = geometry.vertices; - var nbVertices = vertices.length; - - for ( var i = 0; i < nbVertices - 1; i += step ) { - - var distSq = _ray$1.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); - - if ( distSq > localThresholdSq ) { continue; } - - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - - var distance = raycaster.ray.origin.distanceTo( interRay ); - - if ( distance < raycaster.near || distance > raycaster.far ) { continue; } - - intersects.push( { - - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this - - } ); - - } - - } - - }, - - updateMorphTargets: function () { - - var geometry = this.geometry; - var m, ml, name; - - if ( geometry.isBufferGeometry ) { - - var morphAttributes = geometry.morphAttributes; - var keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - var morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - var morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - }, - - clone: function () { - - return new this.constructor( this.geometry, this.material ).copy( this ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var _start$1 = new Vector3(); - var _end$1 = new Vector3(); - - function LineSegments( geometry, material ) { - - Line.call( this, geometry, material ); - - this.type = 'LineSegments'; - - } - - LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { - - constructor: LineSegments, - - isLineSegments: true, - - computeLineDistances: function () { - - var geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - // we assume non-indexed geometry - - if ( geometry.index === null ) { - - var positionAttribute = geometry.attributes.position; - var lineDistances = []; - - for ( var i = 0, l = positionAttribute.count; i < l; i += 2 ) { - - _start$1.fromBufferAttribute( positionAttribute, i ); - _end$1.fromBufferAttribute( positionAttribute, i + 1 ); - - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 ); - - } - - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - - } else { - - console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - - } - - } else if ( geometry.isGeometry ) { - - var vertices = geometry.vertices; - var lineDistances = geometry.lineDistances; - - for ( var i = 0, l = vertices.length; i < l; i += 2 ) { - - _start$1.copy( vertices[ i ] ); - _end$1.copy( vertices[ i + 1 ] ); - - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 ); - - } - - } - - return this; - - } - - } ); - - /** - * @author mgreter / http://github.com/mgreter - */ - - function LineLoop( geometry, material ) { - - Line.call( this, geometry, material ); - - this.type = 'LineLoop'; - - } - - LineLoop.prototype = Object.assign( Object.create( Line.prototype ), { - - constructor: LineLoop, - - isLineLoop: true, - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: - * - * morphTargets: - * } - */ - - function PointsMaterial( parameters ) { - - Material.call( this ); - - this.type = 'PointsMaterial'; - - this.color = new Color( 0xffffff ); - - this.map = null; - - this.alphaMap = null; - - this.size = 1; - this.sizeAttenuation = true; - - this.morphTargets = false; - - this.setValues( parameters ); - - } - - PointsMaterial.prototype = Object.create( Material.prototype ); - PointsMaterial.prototype.constructor = PointsMaterial; - - PointsMaterial.prototype.isPointsMaterial = true; - - PointsMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.alphaMap = source.alphaMap; - - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; - - this.morphTargets = source.morphTargets; - - return this; - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - var _inverseMatrix$2 = new Matrix4(); - var _ray$2 = new Ray(); - var _sphere$3 = new Sphere(); - var _position$1 = new Vector3(); - - function Points( geometry, material ) { - - Object3D.call( this ); - - this.type = 'Points'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new PointsMaterial(); - - this.updateMorphTargets(); - - } - - Points.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Points, - - isPoints: true, - - raycast: function ( raycaster, intersects ) { - - var geometry = this.geometry; - var matrixWorld = this.matrixWorld; - var threshold = raycaster.params.Points.threshold; - - // Checking boundingSphere distance to ray - - if ( geometry.boundingSphere === null ) { geometry.computeBoundingSphere(); } - - _sphere$3.copy( geometry.boundingSphere ); - _sphere$3.applyMatrix4( matrixWorld ); - _sphere$3.radius += threshold; - - if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) { return; } - - // - - _inverseMatrix$2.getInverse( matrixWorld ); - _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); - - var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - var localThresholdSq = localThreshold * localThreshold; - - if ( geometry.isBufferGeometry ) { - - var index = geometry.index; - var attributes = geometry.attributes; - var positions = attributes.position.array; - - if ( index !== null ) { - - var indices = index.array; - - for ( var i = 0, il = indices.length; i < il; i ++ ) { - - var a = indices[ i ]; - - _position$1.fromArray( positions, a * 3 ); - - testPoint( _position$1, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } else { - - for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { - - _position$1.fromArray( positions, i * 3 ); - - testPoint( _position$1, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } - - } else { - - var vertices = geometry.vertices; - - for ( var i = 0, l = vertices.length; i < l; i ++ ) { - - testPoint( vertices[ i ], i, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } - - } - - }, - - updateMorphTargets: function () { - - var geometry = this.geometry; - var m, ml, name; - - if ( geometry.isBufferGeometry ) { - - var morphAttributes = geometry.morphAttributes; - var keys = Object.keys( morphAttributes ); - - if ( keys.length > 0 ) { - - var morphAttribute = morphAttributes[ keys[ 0 ] ]; - - if ( morphAttribute !== undefined ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - - name = morphAttribute[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } - - } - - } - - } else { - - var morphTargets = geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - - } - - } - - }, - - clone: function () { - - return new this.constructor( this.geometry, this.material ).copy( this ); - - } - - } ); - - function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { - - var rayPointDistanceSq = _ray$2.distanceSqToPoint( point ); - - if ( rayPointDistanceSq < localThresholdSq ) { - - var intersectPoint = new Vector3(); - - _ray$2.closestPointToPoint( point, intersectPoint ); - intersectPoint.applyMatrix4( matrixWorld ); - - var distance = raycaster.ray.origin.distanceTo( intersectPoint ); - - if ( distance < raycaster.near || distance > raycaster.far ) { return; } - - intersects.push( { - - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint, - index: index, - face: null, - object: object - - } ); - - } - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.format = format !== undefined ? format : RGBFormat; - - this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - - this.generateMipmaps = false; - - } - - VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), { - - constructor: VideoTexture, - - isVideoTexture: true, - - update: function () { - - var video = this.image; - - if ( video.readyState >= video.HAVE_CURRENT_DATA ) { - - this.needsUpdate = true; - - } - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; - - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) - - this.flipY = false; - - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files - - this.generateMipmaps = false; - - } - - CompressedTexture.prototype = Object.create( Texture.prototype ); - CompressedTexture.prototype.constructor = CompressedTexture; - - CompressedTexture.prototype.isCompressedTexture = true; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.needsUpdate = true; - - } - - CanvasTexture.prototype = Object.create( Texture.prototype ); - CanvasTexture.prototype.constructor = CanvasTexture; - CanvasTexture.prototype.isCanvasTexture = true; - - /** - * @author Matt DesLauriers / @mattdesl - * @author atix / arthursilber.de - */ - - function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { - - format = format !== undefined ? format : DepthFormat; - - if ( format !== DepthFormat && format !== DepthStencilFormat ) { - - throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); - - } - - if ( type === undefined && format === DepthFormat ) { type = UnsignedShortType; } - if ( type === undefined && format === DepthStencilFormat ) { type = UnsignedInt248Type; } - - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.image = { width: width, height: height }; - - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - - this.flipY = false; - this.generateMipmaps = false; - - } - - DepthTexture.prototype = Object.create( Texture.prototype ); - DepthTexture.prototype.constructor = DepthTexture; - DepthTexture.prototype.isDepthTexture = true; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ - - function WireframeGeometry( geometry ) { - - BufferGeometry.call( this ); - - this.type = 'WireframeGeometry'; - - // buffer - - var vertices = []; - - // helper variables - - var i, j, l, o, ol; - var edge = [ 0, 0 ], edges = {}, e, edge1, edge2; - var key, keys = [ 'a', 'b', 'c' ]; - var vertex; - - // different logic for Geometry and BufferGeometry - - if ( geometry && geometry.isGeometry ) { - - // create a data structure that contains all edges without duplicates - - var faces = geometry.faces; - - for ( i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( j = 0; j < 3; j ++ ) { - - edge1 = face[ keys[ j ] ]; - edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; - edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates - edge[ 1 ] = Math.max( edge1, edge2 ); - - key = edge[ 0 ] + ',' + edge[ 1 ]; - - if ( edges[ key ] === undefined ) { - - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; - - } - - } - - } - - // generate vertices - - for ( key in edges ) { - - e = edges[ key ]; - - vertex = geometry.vertices[ e.index1 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - vertex = geometry.vertices[ e.index2 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } else if ( geometry && geometry.isBufferGeometry ) { - - var position, indices, groups; - var group, start, count; - var index1, index2; - - vertex = new Vector3(); - - if ( geometry.index !== null ) { - - // indexed BufferGeometry - - position = geometry.attributes.position; - indices = geometry.index; - groups = geometry.groups; - - if ( groups.length === 0 ) { - - groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; - - } - - // create a data structure that contains all eges without duplicates - - for ( o = 0, ol = groups.length; o < ol; ++ o ) { - - group = groups[ o ]; - - start = group.start; - count = group.count; - - for ( i = start, l = ( start + count ); i < l; i += 3 ) { - - for ( j = 0; j < 3; j ++ ) { - - edge1 = indices.getX( i + j ); - edge2 = indices.getX( i + ( j + 1 ) % 3 ); - edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates - edge[ 1 ] = Math.max( edge1, edge2 ); - - key = edge[ 0 ] + ',' + edge[ 1 ]; - - if ( edges[ key ] === undefined ) { - - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; - - } - - } - - } - - } - - // generate vertices - - for ( key in edges ) { - - e = edges[ key ]; - - vertex.fromBufferAttribute( position, e.index1 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - vertex.fromBufferAttribute( position, e.index2 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } else { - - // non-indexed BufferGeometry - - position = geometry.attributes.position; - - for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) { - - for ( j = 0; j < 3; j ++ ) { - - // three edges per triangle, an edge is represented as (index1, index2) - // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) - - index1 = 3 * i + j; - vertex.fromBufferAttribute( position, index1 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - index2 = 3 * i + ( ( j + 1 ) % 3 ); - vertex.fromBufferAttribute( position, index2 ); - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - } - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - - } - - WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); - WireframeGeometry.prototype.constructor = WireframeGeometry; - - /** - * @author zz85 / https://github.com/zz85 - * @author Mugen87 / https://github.com/Mugen87 - * - * Parametric Surfaces Geometry - * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html - */ - - // ParametricGeometry - - function ParametricGeometry( func, slices, stacks ) { - - Geometry.call( this ); - - this.type = 'ParametricGeometry'; - - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; - - this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); - this.mergeVertices(); - - } - - ParametricGeometry.prototype = Object.create( Geometry.prototype ); - ParametricGeometry.prototype.constructor = ParametricGeometry; - - // ParametricBufferGeometry - - function ParametricBufferGeometry( func, slices, stacks ) { - - BufferGeometry.call( this ); - - this.type = 'ParametricBufferGeometry'; - - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - var EPS = 0.00001; - - var normal = new Vector3(); - - var p0 = new Vector3(), p1 = new Vector3(); - var pu = new Vector3(), pv = new Vector3(); - - var i, j; - - if ( func.length < 3 ) { - - console.error( 'THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.' ); - - } - - // generate vertices, normals and uvs - - var sliceCount = slices + 1; - - for ( i = 0; i <= stacks; i ++ ) { - - var v = i / stacks; - - for ( j = 0; j <= slices; j ++ ) { - - var u = j / slices; - - // vertex - - func( u, v, p0 ); - vertices.push( p0.x, p0.y, p0.z ); - - // normal - - // approximate tangent vectors via finite differences - - if ( u - EPS >= 0 ) { - - func( u - EPS, v, p1 ); - pu.subVectors( p0, p1 ); - - } else { - - func( u + EPS, v, p1 ); - pu.subVectors( p1, p0 ); - - } - - if ( v - EPS >= 0 ) { - - func( u, v - EPS, p1 ); - pv.subVectors( p0, p1 ); - - } else { - - func( u, v + EPS, p1 ); - pv.subVectors( p1, p0 ); - - } - - // cross product of tangent vectors returns surface normal - - normal.crossVectors( pu, pv ).normalize(); - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( u, v ); - - } - - } - - // generate indices - - for ( i = 0; i < stacks; i ++ ) { - - for ( j = 0; j < slices; j ++ ) { - - var a = i * sliceCount + j; - var b = i * sliceCount + j + 1; - var c = ( i + 1 ) * sliceCount + j + 1; - var d = ( i + 1 ) * sliceCount + j; - - // faces one and two - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; - - /** - * @author clockworkgeek / https://github.com/clockworkgeek - * @author timothypratley / https://github.com/timothypratley - * @author WestLangley / http://github.com/WestLangley - * @author Mugen87 / https://github.com/Mugen87 - */ - - // PolyhedronGeometry - - function PolyhedronGeometry( vertices, indices, radius, detail ) { - - Geometry.call( this ); - - this.type = 'PolyhedronGeometry'; - - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); - this.mergeVertices(); - - } - - PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); - PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; - - // PolyhedronBufferGeometry - - function PolyhedronBufferGeometry( vertices, indices, radius, detail ) { - - BufferGeometry.call( this ); - - this.type = 'PolyhedronBufferGeometry'; - - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; - - radius = radius || 1; - detail = detail || 0; - - // default buffer data - - var vertexBuffer = []; - var uvBuffer = []; - - // the subdivision creates the vertex buffer data - - subdivide( detail ); - - // all vertices should lie on a conceptual sphere with a given radius - - applyRadius( radius ); - - // finally, create the uv data - - generateUVs(); - - // build non-indexed geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) ); - - if ( detail === 0 ) { - - this.computeVertexNormals(); // flat normals - - } else { - - this.normalizeNormals(); // smooth normals - - } - - // helper functions - - function subdivide( detail ) { - - var a = new Vector3(); - var b = new Vector3(); - var c = new Vector3(); - - // iterate over all faces and apply a subdivison with the given detail value - - for ( var i = 0; i < indices.length; i += 3 ) { - - // get the vertices of the face - - getVertexByIndex( indices[ i + 0 ], a ); - getVertexByIndex( indices[ i + 1 ], b ); - getVertexByIndex( indices[ i + 2 ], c ); - - // perform subdivision - - subdivideFace( a, b, c, detail ); - - } - - } - - function subdivideFace( a, b, c, detail ) { - - var cols = Math.pow( 2, detail ); - - // we use this multidimensional array as a data structure for creating the subdivision - - var v = []; - - var i, j; - - // construct all of the vertices for this subdivision - - for ( i = 0; i <= cols; i ++ ) { - - v[ i ] = []; - - var aj = a.clone().lerp( c, i / cols ); - var bj = b.clone().lerp( c, i / cols ); - - var rows = cols - i; - - for ( j = 0; j <= rows; j ++ ) { - - if ( j === 0 && i === cols ) { - - v[ i ][ j ] = aj; - - } else { - - v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); - - } - - } - - } - - // construct all of the faces - - for ( i = 0; i < cols; i ++ ) { - - for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { - - var k = Math.floor( j / 2 ); - - if ( j % 2 === 0 ) { - - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); - pushVertex( v[ i ][ k ] ); - - } else { - - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); - - } - - } - - } - - } - - function applyRadius( radius ) { - - var vertex = new Vector3(); - - // iterate over the entire buffer and apply the radius to each vertex - - for ( var i = 0; i < vertexBuffer.length; i += 3 ) { - - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; - - vertex.normalize().multiplyScalar( radius ); - - vertexBuffer[ i + 0 ] = vertex.x; - vertexBuffer[ i + 1 ] = vertex.y; - vertexBuffer[ i + 2 ] = vertex.z; - - } - - } - - function generateUVs() { - - var vertex = new Vector3(); - - for ( var i = 0; i < vertexBuffer.length; i += 3 ) { - - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; - - var u = azimuth( vertex ) / 2 / Math.PI + 0.5; - var v = inclination( vertex ) / Math.PI + 0.5; - uvBuffer.push( u, 1 - v ); - - } - - correctUVs(); - - correctSeam(); - - } - - function correctSeam() { - - // handle case when face straddles the seam, see #3269 - - for ( var i = 0; i < uvBuffer.length; i += 6 ) { - - // uv data of a single face - - var x0 = uvBuffer[ i + 0 ]; - var x1 = uvBuffer[ i + 2 ]; - var x2 = uvBuffer[ i + 4 ]; - - var max = Math.max( x0, x1, x2 ); - var min = Math.min( x0, x1, x2 ); - - // 0.9 is somewhat arbitrary - - if ( max > 0.9 && min < 0.1 ) { - - if ( x0 < 0.2 ) { uvBuffer[ i + 0 ] += 1; } - if ( x1 < 0.2 ) { uvBuffer[ i + 2 ] += 1; } - if ( x2 < 0.2 ) { uvBuffer[ i + 4 ] += 1; } - - } - - } - - } - - function pushVertex( vertex ) { - - vertexBuffer.push( vertex.x, vertex.y, vertex.z ); - - } - - function getVertexByIndex( index, vertex ) { - - var stride = index * 3; - - vertex.x = vertices[ stride + 0 ]; - vertex.y = vertices[ stride + 1 ]; - vertex.z = vertices[ stride + 2 ]; - - } - - function correctUVs() { - - var a = new Vector3(); - var b = new Vector3(); - var c = new Vector3(); - - var centroid = new Vector3(); - - var uvA = new Vector2(); - var uvB = new Vector2(); - var uvC = new Vector2(); - - for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { - - a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); - b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); - c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); - - uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); - uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); - uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); - - centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); - - var azi = azimuth( centroid ); - - correctUV( uvA, j + 0, a, azi ); - correctUV( uvB, j + 2, b, azi ); - correctUV( uvC, j + 4, c, azi ); - - } - - } - - function correctUV( uv, stride, vector, azimuth ) { - - if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { - - uvBuffer[ stride ] = uv.x - 1; - - } - - if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { - - uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; - - } - - } - - // Angle around the Y axis, counter-clockwise when looking from above. - - function azimuth( vector ) { - - return Math.atan2( vector.z, - vector.x ); - - } - - - // Angle above the XZ plane. - - function inclination( vector ) { - - return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); - - } - - } - - PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry; - - /** - * @author timothypratley / https://github.com/timothypratley - * @author Mugen87 / https://github.com/Mugen87 - */ - - // TetrahedronGeometry - - function TetrahedronGeometry( radius, detail ) { - - Geometry.call( this ); - - this.type = 'TetrahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - TetrahedronGeometry.prototype = Object.create( Geometry.prototype ); - TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; - - // TetrahedronBufferGeometry - - function TetrahedronBufferGeometry( radius, detail ) { - - var vertices = [ - 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 - ]; - - var indices = [ - 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 - ]; - - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); - - this.type = 'TetrahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry; - - /** - * @author timothypratley / https://github.com/timothypratley - * @author Mugen87 / https://github.com/Mugen87 - */ - - // OctahedronGeometry - - function OctahedronGeometry( radius, detail ) { - - Geometry.call( this ); - - this.type = 'OctahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - OctahedronGeometry.prototype = Object.create( Geometry.prototype ); - OctahedronGeometry.prototype.constructor = OctahedronGeometry; - - // OctahedronBufferGeometry - - function OctahedronBufferGeometry( radius, detail ) { - - var vertices = [ - 1, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, - 1, 0, 0, 0, 1, 0, 0, - 1 - ]; - - var indices = [ - 0, 2, 4, 0, 4, 3, 0, 3, 5, - 0, 5, 2, 1, 2, 5, 1, 5, 3, - 1, 3, 4, 1, 4, 2 - ]; - - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); - - this.type = 'OctahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry; - - /** - * @author timothypratley / https://github.com/timothypratley - * @author Mugen87 / https://github.com/Mugen87 - */ - - // IcosahedronGeometry - - function IcosahedronGeometry( radius, detail ) { - - Geometry.call( this ); - - this.type = 'IcosahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - IcosahedronGeometry.prototype = Object.create( Geometry.prototype ); - IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; - - // IcosahedronBufferGeometry - - function IcosahedronBufferGeometry( radius, detail ) { - - var t = ( 1 + Math.sqrt( 5 ) ) / 2; - - var vertices = [ - - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, - 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, - t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 - ]; - - var indices = [ - 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, - 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, - 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, - 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 - ]; - - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); - - this.type = 'IcosahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry; - - /** - * @author Abe Pazos / https://hamoid.com - * @author Mugen87 / https://github.com/Mugen87 - */ - - // DodecahedronGeometry - - function DodecahedronGeometry( radius, detail ) { - - Geometry.call( this ); - - this.type = 'DodecahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } - - DodecahedronGeometry.prototype = Object.create( Geometry.prototype ); - DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; - - // DodecahedronBufferGeometry - - function DodecahedronBufferGeometry( radius, detail ) { - - var t = ( 1 + Math.sqrt( 5 ) ) / 2; - var r = 1 / t; - - var vertices = [ - - // (±1, ±1, ±1) - - 1, - 1, - 1, - 1, - 1, 1, - - 1, 1, - 1, - 1, 1, 1, - 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, 1, 1, 1, - - // (0, ±1/φ, ±φ) - 0, - r, - t, 0, - r, t, - 0, r, - t, 0, r, t, - - // (±1/φ, ±φ, 0) - - r, - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, - - // (±φ, 0, ±1/φ) - - t, 0, - r, t, 0, - r, - - t, 0, r, t, 0, r - ]; - - var indices = [ - 3, 11, 7, 3, 7, 15, 3, 15, 13, - 7, 19, 17, 7, 17, 6, 7, 6, 15, - 17, 4, 8, 17, 8, 10, 17, 10, 6, - 8, 0, 16, 8, 16, 2, 8, 2, 10, - 0, 12, 1, 0, 1, 18, 0, 18, 16, - 6, 10, 2, 6, 2, 13, 6, 13, 15, - 2, 16, 18, 2, 18, 3, 2, 3, 13, - 18, 1, 9, 18, 9, 11, 18, 11, 3, - 4, 14, 12, 4, 12, 0, 4, 0, 8, - 11, 9, 5, 11, 5, 19, 11, 19, 7, - 19, 5, 14, 19, 14, 4, 19, 4, 17, - 1, 12, 14, 1, 14, 5, 1, 5, 9 - ]; - - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); - - this.type = 'DodecahedronBufferGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; - - } - - DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry; - - /** - * @author oosmoxiecode / https://github.com/oosmoxiecode - * @author WestLangley / https://github.com/WestLangley - * @author zz85 / https://github.com/zz85 - * @author miningold / https://github.com/miningold - * @author jonobr1 / https://github.com/jonobr1 - * @author Mugen87 / https://github.com/Mugen87 - * - */ - - // TubeGeometry - - function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) { - - Geometry.call( this ); - - this.type = 'TubeGeometry'; - - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed - }; - - if ( taper !== undefined ) { console.warn( 'THREE.TubeGeometry: taper has been removed.' ); } - - var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); - - // expose internals - - this.tangents = bufferGeometry.tangents; - this.normals = bufferGeometry.normals; - this.binormals = bufferGeometry.binormals; - - // create geometry - - this.fromBufferGeometry( bufferGeometry ); - this.mergeVertices(); - - } - - TubeGeometry.prototype = Object.create( Geometry.prototype ); - TubeGeometry.prototype.constructor = TubeGeometry; - - // TubeBufferGeometry - - function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) { - - BufferGeometry.call( this ); - - this.type = 'TubeBufferGeometry'; - - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed - }; - - tubularSegments = tubularSegments || 64; - radius = radius || 1; - radialSegments = radialSegments || 8; - closed = closed || false; - - var frames = path.computeFrenetFrames( tubularSegments, closed ); - - // expose internals - - this.tangents = frames.tangents; - this.normals = frames.normals; - this.binormals = frames.binormals; - - // helper variables - - var vertex = new Vector3(); - var normal = new Vector3(); - var uv = new Vector2(); - var P = new Vector3(); - - var i, j; - - // buffer - - var vertices = []; - var normals = []; - var uvs = []; - var indices = []; - - // create buffer data - - generateBufferData(); - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // functions - - function generateBufferData() { - - for ( i = 0; i < tubularSegments; i ++ ) { - - generateSegment( i ); - - } - - // if the geometry is not closed, generate the last row of vertices and normals - // at the regular position on the given path - // - // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) - - generateSegment( ( closed === false ) ? tubularSegments : 0 ); - - // uvs are generated in a separate function. - // this makes it easy compute correct values for closed geometries - - generateUVs(); - - // finally create faces - - generateIndices(); - - } - - function generateSegment( i ) { - - // we use getPointAt to sample evenly distributed points from the given path - - P = path.getPointAt( i / tubularSegments, P ); - - // retrieve corresponding normal and binormal - - var N = frames.normals[ i ]; - var B = frames.binormals[ i ]; - - // generate normals and vertices for the current segment - - for ( j = 0; j <= radialSegments; j ++ ) { - - var v = j / radialSegments * Math.PI * 2; - - var sin = Math.sin( v ); - var cos = - Math.cos( v ); - - // normal - - normal.x = ( cos * N.x + sin * B.x ); - normal.y = ( cos * N.y + sin * B.y ); - normal.z = ( cos * N.z + sin * B.z ); - normal.normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // vertex - - vertex.x = P.x + radius * normal.x; - vertex.y = P.y + radius * normal.y; - vertex.z = P.z + radius * normal.z; - - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - function generateIndices() { - - for ( j = 1; j <= tubularSegments; j ++ ) { - - for ( i = 1; i <= radialSegments; i ++ ) { - - var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - var b = ( radialSegments + 1 ) * j + ( i - 1 ); - var c = ( radialSegments + 1 ) * j + i; - var d = ( radialSegments + 1 ) * ( j - 1 ) + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - } - - function generateUVs() { - - for ( i = 0; i <= tubularSegments; i ++ ) { - - for ( j = 0; j <= radialSegments; j ++ ) { - - uv.x = i / tubularSegments; - uv.y = j / radialSegments; - - uvs.push( uv.x, uv.y ); - - } - - } - - } - - } - - TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TubeBufferGeometry.prototype.constructor = TubeBufferGeometry; - - TubeBufferGeometry.prototype.toJSON = function () { - - var data = BufferGeometry.prototype.toJSON.call( this ); - - data.path = this.parameters.path.toJSON(); - - return data; - - }; - - /** - * @author oosmoxiecode - * @author Mugen87 / https://github.com/Mugen87 - * - * based on http://www.blackpawn.com/texts/pqtorus/ - */ - - // TorusKnotGeometry - - function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { - - Geometry.call( this ); - - this.type = 'TorusKnotGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; - - if ( heightScale !== undefined ) { console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); } - - this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); - this.mergeVertices(); - - } - - TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); - TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; - - // TorusKnotBufferGeometry - - function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { - - BufferGeometry.call( this ); - - this.type = 'TorusKnotBufferGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; - - radius = radius || 1; - tube = tube || 0.4; - tubularSegments = Math.floor( tubularSegments ) || 64; - radialSegments = Math.floor( radialSegments ) || 8; - p = p || 2; - q = q || 3; - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - // helper variables - - var i, j; - - var vertex = new Vector3(); - var normal = new Vector3(); - - var P1 = new Vector3(); - var P2 = new Vector3(); - - var B = new Vector3(); - var T = new Vector3(); - var N = new Vector3(); - - // generate vertices, normals and uvs - - for ( i = 0; i <= tubularSegments; ++ i ) { - - // the radian "u" is used to calculate the position on the torus curve of the current tubular segement - - var u = i / tubularSegments * p * Math.PI * 2; - - // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. - // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions - - calculatePositionOnCurve( u, p, q, radius, P1 ); - calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); - - // calculate orthonormal basis - - T.subVectors( P2, P1 ); - N.addVectors( P2, P1 ); - B.crossVectors( T, N ); - N.crossVectors( B, T ); - - // normalize B, N. T can be ignored, we don't use it - - B.normalize(); - N.normalize(); - - for ( j = 0; j <= radialSegments; ++ j ) { - - // now calculate the vertices. they are nothing more than an extrusion of the torus curve. - // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. - - var v = j / radialSegments * Math.PI * 2; - var cx = - tube * Math.cos( v ); - var cy = tube * Math.sin( v ); - - // now calculate the final vertex position. - // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve - - vertex.x = P1.x + ( cx * N.x + cy * B.x ); - vertex.y = P1.y + ( cx * N.y + cy * B.y ); - vertex.z = P1.z + ( cx * N.z + cy * B.z ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) - - normal.subVectors( vertex, P1 ).normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( i / tubularSegments ); - uvs.push( j / radialSegments ); - - } - - } - - // generate indices - - for ( j = 1; j <= tubularSegments; j ++ ) { - - for ( i = 1; i <= radialSegments; i ++ ) { - - // indices - - var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - var b = ( radialSegments + 1 ) * j + ( i - 1 ); - var c = ( radialSegments + 1 ) * j + i; - var d = ( radialSegments + 1 ) * ( j - 1 ) + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // this function calculates the current position on the torus curve - - function calculatePositionOnCurve( u, p, q, radius, position ) { - - var cu = Math.cos( u ); - var su = Math.sin( u ); - var quOverP = q / p * u; - var cs = Math.cos( quOverP ); - - position.x = radius * ( 2 + cs ) * 0.5 * cu; - position.y = radius * ( 2 + cs ) * su * 0.5; - position.z = radius * Math.sin( quOverP ) * 0.5; - - } - - } - - TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; - - /** - * @author oosmoxiecode - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ - - // TorusGeometry - - function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { - - Geometry.call( this ); - - this.type = 'TorusGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; - - this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); - this.mergeVertices(); - - } - - TorusGeometry.prototype = Object.create( Geometry.prototype ); - TorusGeometry.prototype.constructor = TorusGeometry; - - // TorusBufferGeometry - - function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { - - BufferGeometry.call( this ); - - this.type = 'TorusBufferGeometry'; - - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; - - radius = radius || 1; - tube = tube || 0.4; - radialSegments = Math.floor( radialSegments ) || 8; - tubularSegments = Math.floor( tubularSegments ) || 6; - arc = arc || Math.PI * 2; - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - // helper variables - - var center = new Vector3(); - var vertex = new Vector3(); - var normal = new Vector3(); - - var j, i; - - // generate vertices, normals and uvs - - for ( j = 0; j <= radialSegments; j ++ ) { - - for ( i = 0; i <= tubularSegments; i ++ ) { - - var u = i / tubularSegments * arc; - var v = j / radialSegments * Math.PI * 2; - - // vertex - - vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); - vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); - vertex.z = tube * Math.sin( v ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - center.x = radius * Math.cos( u ); - center.y = radius * Math.sin( u ); - normal.subVectors( vertex, center ).normalize(); - - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( i / tubularSegments ); - uvs.push( j / radialSegments ); - - } - - } - - // generate indices - - for ( j = 1; j <= radialSegments; j ++ ) { - - for ( i = 1; i <= tubularSegments; i ++ ) { - - // indices - - var a = ( tubularSegments + 1 ) * j + i - 1; - var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; - var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; - var d = ( tubularSegments + 1 ) * j + i; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; - - /** - * @author Mugen87 / https://github.com/Mugen87 - * Port from https://github.com/mapbox/earcut (v2.2.2) - */ - - var Earcut = { - - triangulate: function ( data, holeIndices, dim ) { - - dim = dim || 2; - - var hasHoles = holeIndices && holeIndices.length, - outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length, - outerNode = linkedList( data, 0, outerLen, dim, true ), - triangles = []; - - if ( ! outerNode || outerNode.next === outerNode.prev ) { return triangles; } - - var minX, minY, maxX, maxY, x, y, invSize; - - if ( hasHoles ) { outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); } - - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - if ( data.length > 80 * dim ) { - - minX = maxX = data[ 0 ]; - minY = maxY = data[ 1 ]; - - for ( var i = dim; i < outerLen; i += dim ) { - - x = data[ i ]; - y = data[ i + 1 ]; - if ( x < minX ) { minX = x; } - if ( y < minY ) { minY = y; } - if ( x > maxX ) { maxX = x; } - if ( y > maxY ) { maxY = y; } - - } - - // minX, minY and invSize are later used to transform coords into integers for z-order calculation - invSize = Math.max( maxX - minX, maxY - minY ); - invSize = invSize !== 0 ? 1 / invSize : 0; - - } - - earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); - - return triangles; - - } - - }; - - // create a circular doubly linked list from polygon points in the specified winding order - function linkedList( data, start, end, dim, clockwise ) { - - var i, last; - - if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { - - for ( i = start; i < end; i += dim ) { last = insertNode( i, data[ i ], data[ i + 1 ], last ); } - - } else { - - for ( i = end - dim; i >= start; i -= dim ) { last = insertNode( i, data[ i ], data[ i + 1 ], last ); } - - } - - if ( last && equals( last, last.next ) ) { - - removeNode( last ); - last = last.next; - - } - - return last; - - } - - // eliminate colinear or duplicate points - function filterPoints( start, end ) { - - if ( ! start ) { return start; } - if ( ! end ) { end = start; } - - var p = start, - again; - do { - - again = false; - - if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { - - removeNode( p ); - p = end = p.prev; - if ( p === p.next ) { break; } - again = true; - - } else { - - p = p.next; - - } - - } while ( again || p !== end ); - - return end; - - } - - // main ear slicing loop which triangulates a polygon (given as a linked list) - function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { - - if ( ! ear ) { return; } - - // interlink polygon nodes in z-order - if ( ! pass && invSize ) { indexCurve( ear, minX, minY, invSize ); } - - var stop = ear, - prev, next; - - // iterate through ears, slicing them one by one - while ( ear.prev !== ear.next ) { - - prev = ear.prev; - next = ear.next; - - if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { - - // cut off the triangle - triangles.push( prev.i / dim ); - triangles.push( ear.i / dim ); - triangles.push( next.i / dim ); - - removeNode( ear ); - - // skipping the next vertex leads to less sliver triangles - ear = next.next; - stop = next.next; - - continue; - - } - - ear = next; - - // if we looped through the whole remaining polygon and can't find any more ears - if ( ear === stop ) { - - // try filtering points and slicing again - if ( ! pass ) { - - earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); - - // if this didn't work, try curing all small self-intersections locally - - } else if ( pass === 1 ) { - - ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); - earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); - - // as a last resort, try splitting the remaining polygon into two - - } else if ( pass === 2 ) { - - splitEarcut( ear, triangles, dim, minX, minY, invSize ); - - } - - break; - - } - - } - - } - - // check whether a polygon node forms a valid ear with adjacent nodes - function isEar( ear ) { - - var a = ear.prev, - b = ear, - c = ear.next; - - if ( area( a, b, c ) >= 0 ) { return false; } // reflex, can't be an ear - - // now make sure we don't have other points inside the potential ear - var p = ear.next.next; - - while ( p !== ear.prev ) { - - if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) { return false; } - p = p.next; - - } - - return true; - - } - - function isEarHashed( ear, minX, minY, invSize ) { - - var a = ear.prev, - b = ear, - c = ear.next; - - if ( area( a, b, c ) >= 0 ) { return false; } // reflex, can't be an ear - - // triangle bbox; min & max are calculated like this for speed - var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), - minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), - maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), - maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); - - // z-order range for the current triangle bbox; - var minZ = zOrder( minTX, minTY, minX, minY, invSize ), - maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); - - var p = ear.prevZ, - n = ear.nextZ; - - // look for points inside the triangle in both directions - while ( p && p.z >= minZ && n && n.z <= maxZ ) { - - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) { return false; } - p = p.prevZ; - - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) { return false; } - n = n.nextZ; - - } - - // look for remaining points in decreasing z-order - while ( p && p.z >= minZ ) { - - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) { return false; } - p = p.prevZ; - - } - - // look for remaining points in increasing z-order - while ( n && n.z <= maxZ ) { - - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) { return false; } - n = n.nextZ; - - } - - return true; - - } - - // go through all polygon nodes and cure small local self-intersections - function cureLocalIntersections( start, triangles, dim ) { - - var p = start; - do { - - var a = p.prev, - b = p.next.next; - - if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { - - triangles.push( a.i / dim ); - triangles.push( p.i / dim ); - triangles.push( b.i / dim ); - - // remove two nodes involved - removeNode( p ); - removeNode( p.next ); - - p = start = b; - - } - - p = p.next; - - } while ( p !== start ); - - return filterPoints( p ); - - } - - // try splitting polygon into two and triangulate them independently - function splitEarcut( start, triangles, dim, minX, minY, invSize ) { - - // look for a valid diagonal that divides the polygon into two - var a = start; - do { - - var b = a.next.next; - while ( b !== a.prev ) { - - if ( a.i !== b.i && isValidDiagonal( a, b ) ) { - - // split the polygon in two by the diagonal - var c = splitPolygon( a, b ); - - // filter colinear points around the cuts - a = filterPoints( a, a.next ); - c = filterPoints( c, c.next ); - - // run earcut on each half - earcutLinked( a, triangles, dim, minX, minY, invSize ); - earcutLinked( c, triangles, dim, minX, minY, invSize ); - return; - - } - - b = b.next; - - } - - a = a.next; - - } while ( a !== start ); - - } - - // link every hole into the outer loop, producing a single-ring polygon without holes - function eliminateHoles( data, holeIndices, outerNode, dim ) { - - var queue = [], - i, len, start, end, list; - - for ( i = 0, len = holeIndices.length; i < len; i ++ ) { - - start = holeIndices[ i ] * dim; - end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; - list = linkedList( data, start, end, dim, false ); - if ( list === list.next ) { list.steiner = true; } - queue.push( getLeftmost( list ) ); - - } - - queue.sort( compareX ); - - // process holes from left to right - for ( i = 0; i < queue.length; i ++ ) { - - eliminateHole( queue[ i ], outerNode ); - outerNode = filterPoints( outerNode, outerNode.next ); - - } - - return outerNode; - - } - - function compareX( a, b ) { - - return a.x - b.x; - - } - - // find a bridge between vertices that connects hole with an outer ring and and link it - function eliminateHole( hole, outerNode ) { - - outerNode = findHoleBridge( hole, outerNode ); - if ( outerNode ) { - - var b = splitPolygon( outerNode, hole ); - - // filter collinear points around the cuts - filterPoints( outerNode, outerNode.next ); - filterPoints( b, b.next ); - - } - - } - - // David Eberly's algorithm for finding a bridge between hole and outer polygon - function findHoleBridge( hole, outerNode ) { - - var p = outerNode, - hx = hole.x, - hy = hole.y, - qx = - Infinity, - m; - - // find a segment intersected by a ray from the hole's leftmost point to the left; - // segment's endpoint with lesser x will be potential connection point - do { - - if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { - - var x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); - if ( x <= hx && x > qx ) { - - qx = x; - if ( x === hx ) { - - if ( hy === p.y ) { return p; } - if ( hy === p.next.y ) { return p.next; } - - } - - m = p.x < p.next.x ? p : p.next; - - } - - } - - p = p.next; - - } while ( p !== outerNode ); - - if ( ! m ) { return null; } - - if ( hx === qx ) { return m; } // hole touches outer segment; pick leftmost endpoint - - // look for points inside the triangle of hole point, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the point of the minimum angle with the ray as connection point - - var stop = m, - mx = m.x, - my = m.y, - tanMin = Infinity, - tan; - - p = m; - - do { - - if ( hx >= p.x && p.x >= mx && hx !== p.x && - pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { - - tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential - - if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { - - m = p; - tanMin = tan; - - } - - } - - p = p.next; - - } while ( p !== stop ); - - return m; - - } - - // whether sector in vertex m contains sector in vertex p in the same coordinates - function sectorContainsSector( m, p ) { - - return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; - - } - - // interlink polygon nodes in z-order - function indexCurve( start, minX, minY, invSize ) { - - var p = start; - do { - - if ( p.z === null ) { p.z = zOrder( p.x, p.y, minX, minY, invSize ); } - p.prevZ = p.prev; - p.nextZ = p.next; - p = p.next; - - } while ( p !== start ); - - p.prevZ.nextZ = null; - p.prevZ = null; - - sortLinked( p ); - - } - - // Simon Tatham's linked list merge sort algorithm - // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html - function sortLinked( list ) { - - var i, p, q, e, tail, numMerges, pSize, qSize, - inSize = 1; - - do { - - p = list; - list = null; - tail = null; - numMerges = 0; - - while ( p ) { - - numMerges ++; - q = p; - pSize = 0; - for ( i = 0; i < inSize; i ++ ) { - - pSize ++; - q = q.nextZ; - if ( ! q ) { break; } - - } - - qSize = inSize; - - while ( pSize > 0 || ( qSize > 0 && q ) ) { - - if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { - - e = p; - p = p.nextZ; - pSize --; - - } else { - - e = q; - q = q.nextZ; - qSize --; - - } - - if ( tail ) { tail.nextZ = e; } - else { list = e; } - - e.prevZ = tail; - tail = e; - - } - - p = q; - - } - - tail.nextZ = null; - inSize *= 2; - - } while ( numMerges > 1 ); - - return list; - - } - - // z-order of a point given coords and inverse of the longer side of data bbox - function zOrder( x, y, minX, minY, invSize ) { - - // coords are transformed into non-negative 15-bit integer range - x = 32767 * ( x - minX ) * invSize; - y = 32767 * ( y - minY ) * invSize; - - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; - - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; - - return x | ( y << 1 ); - - } - - // find the leftmost node of a polygon ring - function getLeftmost( start ) { - - var p = start, - leftmost = start; - do { - - if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) { leftmost = p; } - p = p.next; - - } while ( p !== start ); - - return leftmost; - - } - - // check if a point lies within a convex triangle - function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { - - return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && - ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && - ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; - - } - - // check if a diagonal between two polygon nodes is valid (lies in polygon interior) - function isValidDiagonal( a, b ) { - - return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones't intersect other edges - ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible - ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors - equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case - - } - - // signed area of a triangle - function area( p, q, r ) { - - return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); - - } - - // check if two points are equal - function equals( p1, p2 ) { - - return p1.x === p2.x && p1.y === p2.y; - - } - - // check if two segments intersect - function intersects( p1, q1, p2, q2 ) { - - var o1 = sign( area( p1, q1, p2 ) ); - var o2 = sign( area( p1, q1, q2 ) ); - var o3 = sign( area( p2, q2, p1 ) ); - var o4 = sign( area( p2, q2, q1 ) ); - - if ( o1 !== o2 && o3 !== o4 ) { return true; } // general case - - if ( o1 === 0 && onSegment( p1, p2, q1 ) ) { return true; } // p1, q1 and p2 are collinear and p2 lies on p1q1 - if ( o2 === 0 && onSegment( p1, q2, q1 ) ) { return true; } // p1, q1 and q2 are collinear and q2 lies on p1q1 - if ( o3 === 0 && onSegment( p2, p1, q2 ) ) { return true; } // p2, q2 and p1 are collinear and p1 lies on p2q2 - if ( o4 === 0 && onSegment( p2, q1, q2 ) ) { return true; } // p2, q2 and q1 are collinear and q1 lies on p2q2 - - return false; - - } - - // for collinear points p, q, r, check if point q lies on segment pr - function onSegment( p, q, r ) { - - return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); - - } - - function sign( num ) { - - return num > 0 ? 1 : num < 0 ? - 1 : 0; - - } - - // check if a polygon diagonal intersects any polygon segments - function intersectsPolygon( a, b ) { - - var p = a; - do { - - if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && - intersects( p, p.next, a, b ) ) { return true; } - p = p.next; - - } while ( p !== a ); - - return false; - - } - - // check if a polygon diagonal is locally inside the polygon - function locallyInside( a, b ) { - - return area( a.prev, a, a.next ) < 0 ? - area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : - area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; - - } - - // check if the middle point of a polygon diagonal is inside the polygon - function middleInside( a, b ) { - - var p = a, - inside = false, - px = ( a.x + b.x ) / 2, - py = ( a.y + b.y ) / 2; - do { - - if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && - ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) - { inside = ! inside; } - p = p.next; - - } while ( p !== a ); - - return inside; - - } - - // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; - // if one belongs to the outer ring and another to a hole, it merges it into a single ring - function splitPolygon( a, b ) { - - var a2 = new Node( a.i, a.x, a.y ), - b2 = new Node( b.i, b.x, b.y ), - an = a.next, - bp = b.prev; - - a.next = b; - b.prev = a; - - a2.next = an; - an.prev = a2; - - b2.next = a2; - a2.prev = b2; - - bp.next = b2; - b2.prev = bp; - - return b2; - - } - - // create a node and optionally link it with previous one (in a circular doubly linked list) - function insertNode( i, x, y, last ) { - - var p = new Node( i, x, y ); - - if ( ! last ) { - - p.prev = p; - p.next = p; - - } else { - - p.next = last.next; - p.prev = last; - last.next.prev = p; - last.next = p; - - } - - return p; - - } - - function removeNode( p ) { - - p.next.prev = p.prev; - p.prev.next = p.next; - - if ( p.prevZ ) { p.prevZ.nextZ = p.nextZ; } - if ( p.nextZ ) { p.nextZ.prevZ = p.prevZ; } - - } - - function Node( i, x, y ) { - - // vertex index in coordinates array - this.i = i; - - // vertex coordinates - this.x = x; - this.y = y; - - // previous and next vertex nodes in a polygon ring - this.prev = null; - this.next = null; - - // z-order curve value - this.z = null; - - // previous and next nodes in z-order - this.prevZ = null; - this.nextZ = null; - - // indicates whether this is a steiner point - this.steiner = false; - - } - - function signedArea( data, start, end, dim ) { - - var sum = 0; - for ( var i = start, j = end - dim; i < end; i += dim ) { - - sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); - j = i; - - } - - return sum; - - } - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - */ - - var ShapeUtils = { - - // calculate area of the contour polygon - - area: function ( contour ) { - - var n = contour.length; - var a = 0.0; - - for ( var p = n - 1, q = 0; q < n; p = q ++ ) { - - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; - - } - - return a * 0.5; - - }, - - isClockWise: function ( pts ) { - - return ShapeUtils.area( pts ) < 0; - - }, - - triangulateShape: function ( contour, holes ) { - - var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] - var holeIndices = []; // array of hole indices - var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] - - removeDupEndPts( contour ); - addContour( vertices, contour ); - - // - - var holeIndex = contour.length; - - holes.forEach( removeDupEndPts ); - - for ( var i = 0; i < holes.length; i ++ ) { - - holeIndices.push( holeIndex ); - holeIndex += holes[ i ].length; - addContour( vertices, holes[ i ] ); - - } - - // - - var triangles = Earcut.triangulate( vertices, holeIndices ); - - // - - for ( var i = 0; i < triangles.length; i += 3 ) { - - faces.push( triangles.slice( i, i + 3 ) ); - - } - - return faces; - - } - - }; - - function removeDupEndPts( points ) { - - var l = points.length; - - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { - - points.pop(); - - } - - } - - function addContour( vertices, contour ) { - - for ( var i = 0; i < contour.length; i ++ ) { - - vertices.push( contour[ i ].x ); - vertices.push( contour[ i ].y ); - - } - - } - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * depth: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline (including bevelOffset) is bevel - * bevelOffset: , // how far from shape outline does bevel start - * bevelSegments: , // number of bevel layers - * - * extrudePath: // curve to extrude shape along - * - * UVGenerator: // object that provides UV generator functions - * - * } - */ - - // ExtrudeGeometry - - function ExtrudeGeometry( shapes, options ) { - - Geometry.call( this ); - - this.type = 'ExtrudeGeometry'; - - this.parameters = { - shapes: shapes, - options: options - }; - - this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) ); - this.mergeVertices(); - - } - - ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); - ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; - - ExtrudeGeometry.prototype.toJSON = function () { - - var data = Geometry.prototype.toJSON.call( this ); - - var shapes = this.parameters.shapes; - var options = this.parameters.options; - - return toJSON( shapes, options, data ); - - }; - - // ExtrudeBufferGeometry - - function ExtrudeBufferGeometry( shapes, options ) { - - BufferGeometry.call( this ); - - this.type = 'ExtrudeBufferGeometry'; - - this.parameters = { - shapes: shapes, - options: options - }; - - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - - var scope = this; - - var verticesArray = []; - var uvArray = []; - - for ( var i = 0, l = shapes.length; i < l; i ++ ) { - - var shape = shapes[ i ]; - addShape( shape ); - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); - - this.computeVertexNormals(); - - // functions - - function addShape( shape ) { - - var placeholder = []; - - // options - - var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - var steps = options.steps !== undefined ? options.steps : 1; - var depth = options.depth !== undefined ? options.depth : 100; - - var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; - var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; - var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; - var bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; - var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - - var extrudePath = options.extrudePath; - - var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; - - // deprecated options - - if ( options.amount !== undefined ) { - - console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); - depth = options.amount; - - } - - // - - var extrudePts, extrudeByPath = false; - var splineTube, binormal, normal, position2; - - if ( extrudePath ) { - - extrudePts = extrudePath.getSpacedPoints( steps ); - - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion - - // SETUP TNB variables - - // TODO1 - have a .isClosed in spline? - - splineTube = extrudePath.computeFrenetFrames( steps, false ); - - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); - - } - - // Safeguards if bevels are not enabled - - if ( ! bevelEnabled ) { - - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; - bevelOffset = 0; - - } - - // Variables initialization - - var ahole, h, hl; // looping of holes - - var shapePoints = shape.extractPoints( curveSegments ); - - var vertices = shapePoints.shape; - var holes = shapePoints.holes; - - var reverse = ! ShapeUtils.isClockWise( vertices ); - - if ( reverse ) { - - vertices = vertices.reverse(); - - // Maybe we should also check if holes are in the opposite direction, just to be safe ... - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - - if ( ShapeUtils.isClockWise( ahole ) ) { - - holes[ h ] = ahole.reverse(); - - } - - } - - } - - - var faces = ShapeUtils.triangulateShape( vertices, holes ); - - /* Vertices */ - - var contour = vertices; // vertices has all points but contour has only points of circumference - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - - vertices = vertices.concat( ahole ); - - } - - - function scalePt2( pt, vec, size ) { - - if ( ! vec ) { console.error( "THREE.ExtrudeGeometry: vec does not exist" ); } - - return vec.clone().multiplyScalar( size ).add( pt ); - - } - - var b, bs, t, z, - vert, vlen = vertices.length, - face, flen = faces.length; - - - // Find directions for point movement - - - function getBevelVec( inPt, inPrev, inNext ) { - - // computes for inPt the corresponding point inPt' on a new contour - // shifted by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. - - var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt - - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html - - var v_prev_x = inPt.x - inPrev.x, - v_prev_y = inPt.y - inPrev.y; - var v_next_x = inNext.x - inPt.x, - v_next_y = inNext.y - inPt.y; - - var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - - // check for collinear edges - var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - if ( Math.abs( collinear0 ) > Number.EPSILON ) { - - // not collinear - - // length of vectors for normalizing - - var v_prev_len = Math.sqrt( v_prev_lensq ); - var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - - // shift adjacent points by unit vectors to the left - - var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - - var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - - // scaling factor for v_prev to intersection point - - var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - // vector from inPt to intersection point - - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); - if ( v_trans_lensq <= 2 ) { - - return new Vector2( v_trans_x, v_trans_y ); - - } else { - - shrink_by = Math.sqrt( v_trans_lensq / 2 ); - - } - - } else { - - // handle special case of collinear edges - - var direction_eq = false; // assumes: opposite - if ( v_prev_x > Number.EPSILON ) { - - if ( v_next_x > Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( v_prev_x < - Number.EPSILON ) { - - if ( v_next_x < - Number.EPSILON ) { - - direction_eq = true; - - } - - } else { - - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { - - direction_eq = true; - - } - - } - - } - - if ( direction_eq ) { - - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); - - } else { - - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); - - } - - } - - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - - } - - - var contourMovements = []; - - for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) { j = 0; } - if ( k === il ) { k = 0; } - - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) - - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - - } - - var holesMovements = [], - oneHoleMovements, verticesMovements = contourMovements.concat(); - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - - oneHoleMovements = []; - - for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - - if ( j === il ) { j = 0; } - if ( k === il ) { k = 0; } - - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - - } - - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); - - } - - - // Loop bevelSegments, 1 for the front, 1 for the back - - for ( b = 0; b < bevelSegments; b ++ ) { - - //for ( b = bevelSegments; b > 0; b -- ) { - - t = b / bevelSegments; - z = bevelThickness * Math.cos( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - - // contract shape - - for ( i = 0, il = contour.length; i < il; i ++ ) { - - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - // expand holes - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( i = 0, il = ahole.length; i < il; i ++ ) { - - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - v( vert.x, vert.y, - z ); - - } - - } - - } - - bs = bevelSize + bevelOffset; - - // Back facing vertices - - for ( i = 0; i < vlen; i ++ ) { - - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, 0 ); - - } else { - - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - // Add stepped vertices... - // Including front facing vertices - - var s; - - for ( s = 1; s <= steps; s ++ ) { - - for ( i = 0; i < vlen; i ++ ) { - - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, depth / steps * s ); - - } else { - - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); - - v( position2.x, position2.y, position2.z ); - - } - - } - - } - - - // Add bevel segments planes - - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( b = bevelSegments - 1; b >= 0; b -- ) { - - t = b / bevelSegments; - z = bevelThickness * Math.cos( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - - // contract shape - - for ( i = 0, il = contour.length; i < il; i ++ ) { - - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, depth + z ); - - } - - // expand holes - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; - - for ( i = 0, il = ahole.length; i < il; i ++ ) { - - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - - if ( ! extrudeByPath ) { - - v( vert.x, vert.y, depth + z ); - - } else { - - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); - - } - - } - - } - - } - - /* Faces */ - - // Top and bottom faces - - buildLidFaces(); - - // Sides faces - - buildSideFaces(); - - - ///// Internal functions - - function buildLidFaces() { - - var start = verticesArray.length / 3; - - if ( bevelEnabled ) { - - var layer = 0; // steps + 1 - var offset = vlen * layer; - - // Bottom faces - - for ( i = 0; i < flen; i ++ ) { - - face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); - - } - - layer = steps + bevelSegments * 2; - offset = vlen * layer; - - // Top faces - - for ( i = 0; i < flen; i ++ ) { - - face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - - } - - } else { - - // Bottom faces - - for ( i = 0; i < flen; i ++ ) { - - face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - - } - - // Top faces - - for ( i = 0; i < flen; i ++ ) { - - face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - - } - - } - - scope.addGroup( start, verticesArray.length / 3 - start, 0 ); - - } - - // Create faces for the z-sides of the shape - - function buildSideFaces() { - - var start = verticesArray.length / 3; - var layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; - - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - - ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); - - //, true - layeroffset += ahole.length; - - } - - - scope.addGroup( start, verticesArray.length / 3 - start, 1 ); - - - } - - function sidewalls( contour, layeroffset ) { - - var j, k; - i = contour.length; - - while ( -- i >= 0 ) { - - j = i; - k = i - 1; - if ( k < 0 ) { k = contour.length - 1; } - - //console.log('b', i,j, i-1, k,vertices.length); - - var s = 0, - sl = steps + bevelSegments * 2; - - for ( s = 0; s < sl; s ++ ) { - - var slen1 = vlen * s; - var slen2 = vlen * ( s + 1 ); - - var a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; - - f4( a, b, c, d ); - - } - - } - - } - - function v( x, y, z ) { - - placeholder.push( x ); - placeholder.push( y ); - placeholder.push( z ); - - } - - - function f3( a, b, c ) { - - addVertex( a ); - addVertex( b ); - addVertex( c ); - - var nextIndex = verticesArray.length / 3; - var uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - - } - - function f4( a, b, c, d ) { - - addVertex( a ); - addVertex( b ); - addVertex( d ); - - addVertex( b ); - addVertex( c ); - addVertex( d ); - - - var nextIndex = verticesArray.length / 3; - var uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 3 ] ); - - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - addUV( uvs[ 3 ] ); - - } - - function addVertex( index ) { - - verticesArray.push( placeholder[ index * 3 + 0 ] ); - verticesArray.push( placeholder[ index * 3 + 1 ] ); - verticesArray.push( placeholder[ index * 3 + 2 ] ); - - } - - - function addUV( vector2 ) { - - uvArray.push( vector2.x ); - uvArray.push( vector2.y ); - - } - - } - - } - - ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry; - - ExtrudeBufferGeometry.prototype.toJSON = function () { - - var data = BufferGeometry.prototype.toJSON.call( this ); - - var shapes = this.parameters.shapes; - var options = this.parameters.options; - - return toJSON( shapes, options, data ); - - }; - - // - - var WorldUVGenerator = { - - generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { - - var a_x = vertices[ indexA * 3 ]; - var a_y = vertices[ indexA * 3 + 1 ]; - var b_x = vertices[ indexB * 3 ]; - var b_y = vertices[ indexB * 3 + 1 ]; - var c_x = vertices[ indexC * 3 ]; - var c_y = vertices[ indexC * 3 + 1 ]; - - return [ - new Vector2( a_x, a_y ), - new Vector2( b_x, b_y ), - new Vector2( c_x, c_y ) - ]; - - }, - - generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { - - var a_x = vertices[ indexA * 3 ]; - var a_y = vertices[ indexA * 3 + 1 ]; - var a_z = vertices[ indexA * 3 + 2 ]; - var b_x = vertices[ indexB * 3 ]; - var b_y = vertices[ indexB * 3 + 1 ]; - var b_z = vertices[ indexB * 3 + 2 ]; - var c_x = vertices[ indexC * 3 ]; - var c_y = vertices[ indexC * 3 + 1 ]; - var c_z = vertices[ indexC * 3 + 2 ]; - var d_x = vertices[ indexD * 3 ]; - var d_y = vertices[ indexD * 3 + 1 ]; - var d_z = vertices[ indexD * 3 + 2 ]; - - if ( Math.abs( a_y - b_y ) < 0.01 ) { - - return [ - new Vector2( a_x, 1 - a_z ), - new Vector2( b_x, 1 - b_z ), - new Vector2( c_x, 1 - c_z ), - new Vector2( d_x, 1 - d_z ) - ]; - - } else { - - return [ - new Vector2( a_y, 1 - a_z ), - new Vector2( b_y, 1 - b_z ), - new Vector2( c_y, 1 - c_z ), - new Vector2( d_y, 1 - d_z ) - ]; - - } - - } - }; - - function toJSON( shapes, options, data ) { - - // - - data.shapes = []; - - if ( Array.isArray( shapes ) ) { - - for ( var i = 0, l = shapes.length; i < l; i ++ ) { - - var shape = shapes[ i ]; - - data.shapes.push( shape.uuid ); - - } - - } else { - - data.shapes.push( shapes.uuid ); - - } - - // - - if ( options.extrudePath !== undefined ) { data.options.extrudePath = options.extrudePath.toJSON(); } - - return data; - - } - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author alteredq / http://alteredqualia.com/ - * - * Text = 3D Text - * - * parameters = { - * font: , // font - * - * size: , // size of the text - * height: , // thickness to extrude text - * curveSegments: , // number of points on the curves - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into text bevel goes - * bevelSize: , // how far from text outline (including bevelOffset) is bevel - * bevelOffset: // how far from text outline does bevel start - * } - */ - - // TextGeometry - - function TextGeometry( text, parameters ) { - - Geometry.call( this ); - - this.type = 'TextGeometry'; - - this.parameters = { - text: text, - parameters: parameters - }; - - this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) ); - this.mergeVertices(); - - } - - TextGeometry.prototype = Object.create( Geometry.prototype ); - TextGeometry.prototype.constructor = TextGeometry; - - // TextBufferGeometry - - function TextBufferGeometry( text, parameters ) { - - parameters = parameters || {}; - - var font = parameters.font; - - if ( ! ( font && font.isFont ) ) { - - console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); - return new Geometry(); - - } - - var shapes = font.generateShapes( text, parameters.size ); - - // translate parameters to ExtrudeGeometry API - - parameters.depth = parameters.height !== undefined ? parameters.height : 50; - - // defaults - - if ( parameters.bevelThickness === undefined ) { parameters.bevelThickness = 10; } - if ( parameters.bevelSize === undefined ) { parameters.bevelSize = 8; } - if ( parameters.bevelEnabled === undefined ) { parameters.bevelEnabled = false; } - - ExtrudeBufferGeometry.call( this, shapes, parameters ); - - this.type = 'TextBufferGeometry'; - - } - - TextBufferGeometry.prototype = Object.create( ExtrudeBufferGeometry.prototype ); - TextBufferGeometry.prototype.constructor = TextBufferGeometry; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author benaadams / https://twitter.com/ben_a_adams - * @author Mugen87 / https://github.com/Mugen87 - */ - - // SphereGeometry - - function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - - Geometry.call( this ); - - this.type = 'SphereGeometry'; - - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - SphereGeometry.prototype = Object.create( Geometry.prototype ); - SphereGeometry.prototype.constructor = SphereGeometry; - - // SphereBufferGeometry - - function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - - BufferGeometry.call( this ); - - this.type = 'SphereBufferGeometry'; - - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - radius = radius || 1; - - widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); - heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); - - phiStart = phiStart !== undefined ? phiStart : 0; - phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; - - var thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); - - var ix, iy; - - var index = 0; - var grid = []; - - var vertex = new Vector3(); - var normal = new Vector3(); - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - // generate vertices, normals and uvs - - for ( iy = 0; iy <= heightSegments; iy ++ ) { - - var verticesRow = []; - - var v = iy / heightSegments; - - // special case for the poles - - var uOffset = 0; - - if ( iy == 0 && thetaStart == 0 ) { - - uOffset = 0.5 / widthSegments; - - } else if ( iy == heightSegments && thetaEnd == Math.PI ) { - - uOffset = - 0.5 / widthSegments; - - } - - for ( ix = 0; ix <= widthSegments; ix ++ ) { - - var u = ix / widthSegments; - - // vertex - - vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); - vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normal.copy( vertex ).normalize(); - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( u + uOffset, 1 - v ); - - verticesRow.push( index ++ ); - - } - - grid.push( verticesRow ); - - } - - // indices - - for ( iy = 0; iy < heightSegments; iy ++ ) { - - for ( ix = 0; ix < widthSegments; ix ++ ) { - - var a = grid[ iy ][ ix + 1 ]; - var b = grid[ iy ][ ix ]; - var c = grid[ iy + 1 ][ ix ]; - var d = grid[ iy + 1 ][ ix + 1 ]; - - if ( iy !== 0 || thetaStart > 0 ) { indices.push( a, b, d ); } - if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) { indices.push( b, c, d ); } - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; - - /** - * @author Kaleb Murphy - * @author Mugen87 / https://github.com/Mugen87 - */ - - // RingGeometry - - function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - - Geometry.call( this ); - - this.type = 'RingGeometry'; - - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - RingGeometry.prototype = Object.create( Geometry.prototype ); - RingGeometry.prototype.constructor = RingGeometry; - - // RingBufferGeometry - - function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - - BufferGeometry.call( this ); - - this.type = 'RingBufferGeometry'; - - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - innerRadius = innerRadius || 0.5; - outerRadius = outerRadius || 1; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; - phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - // some helper variables - - var segment; - var radius = innerRadius; - var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); - var vertex = new Vector3(); - var uv = new Vector2(); - var j, i; - - // generate vertices, normals and uvs - - for ( j = 0; j <= phiSegments; j ++ ) { - - for ( i = 0; i <= thetaSegments; i ++ ) { - - // values are generate from the inside of the ring to the outside - - segment = thetaStart + i / thetaSegments * thetaLength; - - // vertex - - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normals.push( 0, 0, 1 ); - - // uv - - uv.x = ( vertex.x / outerRadius + 1 ) / 2; - uv.y = ( vertex.y / outerRadius + 1 ) / 2; - - uvs.push( uv.x, uv.y ); - - } - - // increase the radius for next row of vertices - - radius += radiusStep; - - } - - // indices - - for ( j = 0; j < phiSegments; j ++ ) { - - var thetaSegmentLevel = j * ( thetaSegments + 1 ); - - for ( i = 0; i < thetaSegments; i ++ ) { - - segment = i + thetaSegmentLevel; - - var a = segment; - var b = segment + thetaSegments + 1; - var c = segment + thetaSegments + 2; - var d = segment + 1; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - RingBufferGeometry.prototype.constructor = RingBufferGeometry; - - /** - * @author zz85 / https://github.com/zz85 - * @author bhouston / http://clara.io - * @author Mugen87 / https://github.com/Mugen87 - */ - - // LatheGeometry - - function LatheGeometry( points, segments, phiStart, phiLength ) { - - Geometry.call( this ); - - this.type = 'LatheGeometry'; - - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; - - this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); - this.mergeVertices(); - - } - - LatheGeometry.prototype = Object.create( Geometry.prototype ); - LatheGeometry.prototype.constructor = LatheGeometry; - - // LatheBufferGeometry - - function LatheBufferGeometry( points, segments, phiStart, phiLength ) { - - BufferGeometry.call( this ); - - this.type = 'LatheBufferGeometry'; - - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; - - segments = Math.floor( segments ) || 12; - phiStart = phiStart || 0; - phiLength = phiLength || Math.PI * 2; - - // clamp phiLength so it's in range of [ 0, 2PI ] - - phiLength = MathUtils.clamp( phiLength, 0, Math.PI * 2 ); - - - // buffers - - var indices = []; - var vertices = []; - var uvs = []; - - // helper variables - - var base; - var inverseSegments = 1.0 / segments; - var vertex = new Vector3(); - var uv = new Vector2(); - var i, j; - - // generate vertices and uvs - - for ( i = 0; i <= segments; i ++ ) { - - var phi = phiStart + i * inverseSegments * phiLength; - - var sin = Math.sin( phi ); - var cos = Math.cos( phi ); - - for ( j = 0; j <= ( points.length - 1 ); j ++ ) { - - // vertex - - vertex.x = points[ j ].x * sin; - vertex.y = points[ j ].y; - vertex.z = points[ j ].x * cos; - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // uv - - uv.x = i / segments; - uv.y = j / ( points.length - 1 ); - - uvs.push( uv.x, uv.y ); - - - } - - } - - // indices - - for ( i = 0; i < segments; i ++ ) { - - for ( j = 0; j < ( points.length - 1 ); j ++ ) { - - base = j + i * points.length; - - var a = base; - var b = base + points.length; - var c = base + points.length + 1; - var d = base + 1; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - // generate normals - - this.computeVertexNormals(); - - // if the geometry is closed, we need to average the normals along the seam. - // because the corresponding vertices are identical (but still have different UVs). - - if ( phiLength === Math.PI * 2 ) { - - var normals = this.attributes.normal.array; - var n1 = new Vector3(); - var n2 = new Vector3(); - var n = new Vector3(); - - // this is the buffer offset for the last line of vertices - - base = segments * points.length * 3; - - for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) { - - // select the normal of the vertex in the first line - - n1.x = normals[ j + 0 ]; - n1.y = normals[ j + 1 ]; - n1.z = normals[ j + 2 ]; - - // select the normal of the vertex in the last line - - n2.x = normals[ base + j + 0 ]; - n2.y = normals[ base + j + 1 ]; - n2.z = normals[ base + j + 2 ]; - - // average normals - - n.addVectors( n1, n2 ).normalize(); - - // assign the new values to both normals - - normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; - normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; - normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; - - } - - } - - } - - LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; - - /** - * @author jonobr1 / http://jonobr1.com - * @author Mugen87 / https://github.com/Mugen87 - */ - - // ShapeGeometry - - function ShapeGeometry( shapes, curveSegments ) { - - Geometry.call( this ); - - this.type = 'ShapeGeometry'; - - if ( typeof curveSegments === 'object' ) { - - console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' ); - - curveSegments = curveSegments.curveSegments; - - } - - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; - - this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); - this.mergeVertices(); - - } - - ShapeGeometry.prototype = Object.create( Geometry.prototype ); - ShapeGeometry.prototype.constructor = ShapeGeometry; - - ShapeGeometry.prototype.toJSON = function () { - - var data = Geometry.prototype.toJSON.call( this ); - - var shapes = this.parameters.shapes; - - return toJSON$1( shapes, data ); - - }; - - // ShapeBufferGeometry - - function ShapeBufferGeometry( shapes, curveSegments ) { - - BufferGeometry.call( this ); - - this.type = 'ShapeBufferGeometry'; - - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; - - curveSegments = curveSegments || 12; - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - // helper variables - - var groupStart = 0; - var groupCount = 0; - - // allow single and array values for "shapes" parameter - - if ( Array.isArray( shapes ) === false ) { - - addShape( shapes ); - - } else { - - for ( var i = 0; i < shapes.length; i ++ ) { - - addShape( shapes[ i ] ); - - this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support - - groupStart += groupCount; - groupCount = 0; - - } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - - // helper functions - - function addShape( shape ) { - - var i, l, shapeHole; - - var indexOffset = vertices.length / 3; - var points = shape.extractPoints( curveSegments ); - - var shapeVertices = points.shape; - var shapeHoles = points.holes; - - // check direction of vertices - - if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { - - shapeVertices = shapeVertices.reverse(); - - } - - for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { - - shapeHole = shapeHoles[ i ]; - - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { - - shapeHoles[ i ] = shapeHole.reverse(); - - } - - } - - var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); - - // join vertices of inner and outer paths to a single array - - for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { - - shapeHole = shapeHoles[ i ]; - shapeVertices = shapeVertices.concat( shapeHole ); - - } - - // vertices, normals, uvs - - for ( i = 0, l = shapeVertices.length; i < l; i ++ ) { - - var vertex = shapeVertices[ i ]; - - vertices.push( vertex.x, vertex.y, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( vertex.x, vertex.y ); // world uvs - - } - - // incides - - for ( i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - var a = face[ 0 ] + indexOffset; - var b = face[ 1 ] + indexOffset; - var c = face[ 2 ] + indexOffset; - - indices.push( a, b, c ); - groupCount += 3; - - } - - } - - } - - ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry; - - ShapeBufferGeometry.prototype.toJSON = function () { - - var data = BufferGeometry.prototype.toJSON.call( this ); - - var shapes = this.parameters.shapes; - - return toJSON$1( shapes, data ); - - }; - - // - - function toJSON$1( shapes, data ) { - - data.shapes = []; - - if ( Array.isArray( shapes ) ) { - - for ( var i = 0, l = shapes.length; i < l; i ++ ) { - - var shape = shapes[ i ]; - - data.shapes.push( shape.uuid ); - - } - - } else { - - data.shapes.push( shapes.uuid ); - - } - - return data; - - } - - /** - * @author WestLangley / http://github.com/WestLangley - * @author Mugen87 / https://github.com/Mugen87 - */ - - function EdgesGeometry( geometry, thresholdAngle ) { - - BufferGeometry.call( this ); - - this.type = 'EdgesGeometry'; - - this.parameters = { - thresholdAngle: thresholdAngle - }; - - thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; - - // buffer - - var vertices = []; - - // helper variables - - var thresholdDot = Math.cos( MathUtils.DEG2RAD * thresholdAngle ); - var edge = [ 0, 0 ], edges = {}, edge1, edge2; - var key, keys = [ 'a', 'b', 'c' ]; - - // prepare source geometry - - var geometry2; - - if ( geometry.isBufferGeometry ) { - - geometry2 = new Geometry(); - geometry2.fromBufferGeometry( geometry ); - - } else { - - geometry2 = geometry.clone(); - - } - - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); - - var sourceVertices = geometry2.vertices; - var faces = geometry2.faces; - - // now create a data structure where each entry represents an edge with its adjoining faces - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0; j < 3; j ++ ) { - - edge1 = face[ keys[ j ] ]; - edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; - edge[ 0 ] = Math.min( edge1, edge2 ); - edge[ 1 ] = Math.max( edge1, edge2 ); - - key = edge[ 0 ] + ',' + edge[ 1 ]; - - if ( edges[ key ] === undefined ) { - - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined }; - - } else { - - edges[ key ].face2 = i; - - } - - } - - } - - // generate vertices - - for ( key in edges ) { - - var e = edges[ key ]; - - // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. - - if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) { - - var vertex = sourceVertices[ e.index1 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - vertex = sourceVertices[ e.index2 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); - - } - - } - - // build geometry - - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - - } - - EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); - EdgesGeometry.prototype.constructor = EdgesGeometry; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ - - // CylinderGeometry - - function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - Geometry.call( this ); - - this.type = 'CylinderGeometry'; - - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - CylinderGeometry.prototype = Object.create( Geometry.prototype ); - CylinderGeometry.prototype.constructor = CylinderGeometry; - - // CylinderBufferGeometry - - function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - BufferGeometry.call( this ); - - this.type = 'CylinderBufferGeometry'; - - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - var scope = this; - - radiusTop = radiusTop !== undefined ? radiusTop : 1; - radiusBottom = radiusBottom !== undefined ? radiusBottom : 1; - height = height || 1; - - radialSegments = Math.floor( radialSegments ) || 8; - heightSegments = Math.floor( heightSegments ) || 1; - - openEnded = openEnded !== undefined ? openEnded : false; - thetaStart = thetaStart !== undefined ? thetaStart : 0.0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - // helper variables - - var index = 0; - var indexArray = []; - var halfHeight = height / 2; - var groupStart = 0; - - // generate geometry - - generateTorso(); - - if ( openEnded === false ) { - - if ( radiusTop > 0 ) { generateCap( true ); } - if ( radiusBottom > 0 ) { generateCap( false ); } - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - function generateTorso() { - - var x, y; - var normal = new Vector3(); - var vertex = new Vector3(); - - var groupCount = 0; - - // this will be used to calculate the normal - var slope = ( radiusBottom - radiusTop ) / height; - - // generate vertices, normals and uvs - - for ( y = 0; y <= heightSegments; y ++ ) { - - var indexRow = []; - - var v = y / heightSegments; - - // calculate the radius of the current row - - var radius = v * ( radiusBottom - radiusTop ) + radiusTop; - - for ( x = 0; x <= radialSegments; x ++ ) { - - var u = x / radialSegments; - - var theta = u * thetaLength + thetaStart; - - var sinTheta = Math.sin( theta ); - var cosTheta = Math.cos( theta ); - - // vertex - - vertex.x = radius * sinTheta; - vertex.y = - v * height + halfHeight; - vertex.z = radius * cosTheta; - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normal.set( sinTheta, slope, cosTheta ).normalize(); - normals.push( normal.x, normal.y, normal.z ); - - // uv - - uvs.push( u, 1 - v ); - - // save index of vertex in respective row - - indexRow.push( index ++ ); - - } - - // now save vertices of the row in our index array - - indexArray.push( indexRow ); - - } - - // generate indices - - for ( x = 0; x < radialSegments; x ++ ) { - - for ( y = 0; y < heightSegments; y ++ ) { - - // we use the index array to access the correct indices - - var a = indexArray[ y ][ x ]; - var b = indexArray[ y + 1 ][ x ]; - var c = indexArray[ y + 1 ][ x + 1 ]; - var d = indexArray[ y ][ x + 1 ]; - - // faces - - indices.push( a, b, d ); - indices.push( b, c, d ); - - // update group counter - - groupCount += 6; - - } - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, 0 ); - - // calculate new start value for groups - - groupStart += groupCount; - - } - - function generateCap( top ) { - - var x, centerIndexStart, centerIndexEnd; - - var uv = new Vector2(); - var vertex = new Vector3(); - - var groupCount = 0; - - var radius = ( top === true ) ? radiusTop : radiusBottom; - var sign = ( top === true ) ? 1 : - 1; - - // save the index of the first center vertex - centerIndexStart = index; - - // first we generate the center vertex data of the cap. - // because the geometry needs one set of uvs per face, - // we must generate a center vertex per face/segment - - for ( x = 1; x <= radialSegments; x ++ ) { - - // vertex - - vertices.push( 0, halfHeight * sign, 0 ); - - // normal - - normals.push( 0, sign, 0 ); - - // uv - - uvs.push( 0.5, 0.5 ); - - // increase index - - index ++; - - } - - // save the index of the last center vertex - - centerIndexEnd = index; - - // now we generate the surrounding vertices, normals and uvs - - for ( x = 0; x <= radialSegments; x ++ ) { - - var u = x / radialSegments; - var theta = u * thetaLength + thetaStart; - - var cosTheta = Math.cos( theta ); - var sinTheta = Math.sin( theta ); - - // vertex - - vertex.x = radius * sinTheta; - vertex.y = halfHeight * sign; - vertex.z = radius * cosTheta; - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normals.push( 0, sign, 0 ); - - // uv - - uv.x = ( cosTheta * 0.5 ) + 0.5; - uv.y = ( sinTheta * 0.5 * sign ) + 0.5; - uvs.push( uv.x, uv.y ); - - // increase index - - index ++; - - } - - // generate indices - - for ( x = 0; x < radialSegments; x ++ ) { - - var c = centerIndexStart + x; - var i = centerIndexEnd + x; - - if ( top === true ) { - - // face top - - indices.push( i, i + 1, c ); - - } else { - - // face bottom - - indices.push( i + 1, i, c ); - - } - - groupCount += 3; - - } - - // add a group to the geometry. this will ensure multi material support - - scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); - - // calculate new start value for groups - - groupStart += groupCount; - - } - - } - - CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; - - /** - * @author abelnation / http://github.com/abelnation - */ - - // ConeGeometry - - function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); - - this.type = 'ConeGeometry'; - - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - } - - ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); - ConeGeometry.prototype.constructor = ConeGeometry; - - // ConeBufferGeometry - - function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - - CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); - - this.type = 'ConeBufferGeometry'; - - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - } - - ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype ); - ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - * @author Mugen87 / https://github.com/Mugen87 - * @author hughes - */ - - // CircleGeometry - - function CircleGeometry( radius, segments, thetaStart, thetaLength ) { - - Geometry.call( this ); - - this.type = 'CircleGeometry'; - - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); - this.mergeVertices(); - - } - - CircleGeometry.prototype = Object.create( Geometry.prototype ); - CircleGeometry.prototype.constructor = CircleGeometry; - - // CircleBufferGeometry - - function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { - - BufferGeometry.call( this ); - - this.type = 'CircleBufferGeometry'; - - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; - - radius = radius || 1; - segments = segments !== undefined ? Math.max( 3, segments ) : 8; - - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - - // buffers - - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; - - // helper variables - - var i, s; - var vertex = new Vector3(); - var uv = new Vector2(); - - // center point - - vertices.push( 0, 0, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( 0.5, 0.5 ); - - for ( s = 0, i = 3; s <= segments; s ++, i += 3 ) { - - var segment = thetaStart + s / segments * thetaLength; - - // vertex - - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); - - vertices.push( vertex.x, vertex.y, vertex.z ); - - // normal - - normals.push( 0, 0, 1 ); - - // uvs - - uv.x = ( vertices[ i ] / radius + 1 ) / 2; - uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; - - uvs.push( uv.x, uv.y ); - - } - - // indices - - for ( i = 1; i <= segments; i ++ ) { - - indices.push( i, i + 1, 0 ); - - } - - // build geometry - - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; - - var Geometries = /*#__PURE__*/Object.freeze({ - __proto__: null, - WireframeGeometry: WireframeGeometry, - ParametricGeometry: ParametricGeometry, - ParametricBufferGeometry: ParametricBufferGeometry, - TetrahedronGeometry: TetrahedronGeometry, - TetrahedronBufferGeometry: TetrahedronBufferGeometry, - OctahedronGeometry: OctahedronGeometry, - OctahedronBufferGeometry: OctahedronBufferGeometry, - IcosahedronGeometry: IcosahedronGeometry, - IcosahedronBufferGeometry: IcosahedronBufferGeometry, - DodecahedronGeometry: DodecahedronGeometry, - DodecahedronBufferGeometry: DodecahedronBufferGeometry, - PolyhedronGeometry: PolyhedronGeometry, - PolyhedronBufferGeometry: PolyhedronBufferGeometry, - TubeGeometry: TubeGeometry, - TubeBufferGeometry: TubeBufferGeometry, - TorusKnotGeometry: TorusKnotGeometry, - TorusKnotBufferGeometry: TorusKnotBufferGeometry, - TorusGeometry: TorusGeometry, - TorusBufferGeometry: TorusBufferGeometry, - TextGeometry: TextGeometry, - TextBufferGeometry: TextBufferGeometry, - SphereGeometry: SphereGeometry, - SphereBufferGeometry: SphereBufferGeometry, - RingGeometry: RingGeometry, - RingBufferGeometry: RingBufferGeometry, - PlaneGeometry: PlaneGeometry, - PlaneBufferGeometry: PlaneBufferGeometry, - LatheGeometry: LatheGeometry, - LatheBufferGeometry: LatheBufferGeometry, - ShapeGeometry: ShapeGeometry, - ShapeBufferGeometry: ShapeBufferGeometry, - ExtrudeGeometry: ExtrudeGeometry, - ExtrudeBufferGeometry: ExtrudeBufferGeometry, - EdgesGeometry: EdgesGeometry, - ConeGeometry: ConeGeometry, - ConeBufferGeometry: ConeBufferGeometry, - CylinderGeometry: CylinderGeometry, - CylinderBufferGeometry: CylinderBufferGeometry, - CircleGeometry: CircleGeometry, - CircleBufferGeometry: CircleBufferGeometry, - BoxGeometry: BoxGeometry, - BoxBufferGeometry: BoxBufferGeometry - }); - - /** - * @author mrdoob / http://mrdoob.com/ - * - * parameters = { - * color: - * } - */ - - function ShadowMaterial( parameters ) { - - Material.call( this ); - - this.type = 'ShadowMaterial'; - - this.color = new Color( 0x000000 ); - this.transparent = true; - - this.setValues( parameters ); - - } - - ShadowMaterial.prototype = Object.create( Material.prototype ); - ShadowMaterial.prototype.constructor = ShadowMaterial; - - ShadowMaterial.prototype.isShadowMaterial = true; - - ShadowMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function RawShaderMaterial( parameters ) { - - ShaderMaterial.call( this, parameters ); - - this.type = 'RawShaderMaterial'; - - } - - RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); - RawShaderMaterial.prototype.constructor = RawShaderMaterial; - - RawShaderMaterial.prototype.isRawShaderMaterial = true; - - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * color: , - * roughness: , - * metalness: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * roughnessMap: new THREE.Texture( ), - * - * metalnessMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * envMapIntensity: - * - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshStandardMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'STANDARD': '' }; - - this.type = 'MeshStandardMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 1.0; - this.metalness = 0.0; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.roughnessMap = null; - - this.metalnessMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.envMapIntensity = 1.0; - - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.vertexTangents = false; - - this.setValues( parameters ); - - } - - MeshStandardMaterial.prototype = Object.create( Material.prototype ); - MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; - - MeshStandardMaterial.prototype.isMeshStandardMaterial = true; - - MeshStandardMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.defines = { 'STANDARD': '' }; - - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.roughnessMap = source.roughnessMap; - - this.metalnessMap = source.metalnessMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; - - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - this.vertexTangents = source.vertexTangents; - - return this; - - }; - - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * clearcoat: , - * clearcoatMap: new THREE.Texture( ), - * clearcoatRoughness: , - * clearcoatRoughnessMap: new THREE.Texture( ), - * clearcoatNormalScale: , - * clearcoatNormalMap: new THREE.Texture( ), - * - * reflectivity: , - * - * sheen: , - * - * transparency: - * } - */ - - function MeshPhysicalMaterial( parameters ) { - - MeshStandardMaterial.call( this ); - - this.defines = { - - 'STANDARD': '', - 'PHYSICAL': '' - - }; - - this.type = 'MeshPhysicalMaterial'; - - this.clearcoat = 0.0; - this.clearcoatMap = null; - this.clearcoatRoughness = 0.0; - this.clearcoatRoughnessMap = null; - this.clearcoatNormalScale = new Vector2( 1, 1 ); - this.clearcoatNormalMap = null; - - this.reflectivity = 0.5; // maps to F0 = 0.04 - - this.sheen = null; // null will disable sheen bsdf - - this.transparency = 0.0; - - this.setValues( parameters ); - - } - - MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); - MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; - - MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; - - MeshPhysicalMaterial.prototype.copy = function ( source ) { - - MeshStandardMaterial.prototype.copy.call( this, source ); - - this.defines = { - - 'STANDARD': '', - 'PHYSICAL': '' - - }; - - this.clearcoat = source.clearcoat; - this.clearcoatMap = source.clearcoatMap; - this.clearcoatRoughness = source.clearcoatRoughness; - this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; - this.clearcoatNormalMap = source.clearcoatNormalMap; - this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); - - this.reflectivity = source.reflectivity; - - if ( source.sheen ) { - - this.sheen = ( this.sheen || new Color() ).copy( source.sheen ); - - } else { - - this.sheen = null; - - } - - this.transparency = source.transparency; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.MultiplyOperation, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshPhongMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshPhongMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshPhongMaterial.prototype = Object.create( Material.prototype ); - MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; - - MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - - MeshPhongMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * @author takahirox / http://github.com/takahirox - * - * parameters = { - * color: , - * specular: , - * shininess: , - * - * map: new THREE.Texture( ), - * gradientMap: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshToonMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'TOON': '' }; - - this.type = 'MeshToonMaterial'; - - this.color = new Color( 0xffffff ); - this.specular = new Color( 0x111111 ); - this.shininess = 30; - - this.map = null; - this.gradientMap = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.specularMap = null; - - this.alphaMap = null; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshToonMaterial.prototype = Object.create( Material.prototype ); - MeshToonMaterial.prototype.constructor = MeshToonMaterial; - - MeshToonMaterial.prototype.isMeshToonMaterial = true; - - MeshToonMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; - - this.map = source.map; - this.gradientMap = source.gradientMap; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * opacity: , - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshNormalMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshNormalMaterial'; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshNormalMaterial.prototype = Object.create( Material.prototype ); - MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; - - MeshNormalMaterial.prototype.isMeshNormalMaterial = true; - - MeshNormalMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshLambertMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshLambertMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.map = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshLambertMaterial.prototype = Object.create( Material.prototype ); - MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; - - MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - - MeshLambertMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * color: , - * opacity: , - * - * matcap: new THREE.Texture( ), - * - * map: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - function MeshMatcapMaterial( parameters ) { - - Material.call( this ); - - this.defines = { 'MATCAP': '' }; - - this.type = 'MeshMatcapMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.matcap = null; - - this.map = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.alphaMap = null; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - - } - - MeshMatcapMaterial.prototype = Object.create( Material.prototype ); - MeshMatcapMaterial.prototype.constructor = MeshMatcapMaterial; - - MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; - - MeshMatcapMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.defines = { 'MATCAP': '' }; - - this.color.copy( source.color ); - - this.matcap = source.matcap; - - this.map = source.map; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.alphaMap = source.alphaMap; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ - - function LineDashedMaterial( parameters ) { - - LineBasicMaterial.call( this ); - - this.type = 'LineDashedMaterial'; - - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; - - this.setValues( parameters ); - - } - - LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype ); - LineDashedMaterial.prototype.constructor = LineDashedMaterial; - - LineDashedMaterial.prototype.isLineDashedMaterial = true; - - LineDashedMaterial.prototype.copy = function ( source ) { - - LineBasicMaterial.prototype.copy.call( this, source ); - - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; - - return this; - - }; - - var Materials = /*#__PURE__*/Object.freeze({ - __proto__: null, - ShadowMaterial: ShadowMaterial, - SpriteMaterial: SpriteMaterial, - RawShaderMaterial: RawShaderMaterial, - ShaderMaterial: ShaderMaterial, - PointsMaterial: PointsMaterial, - MeshPhysicalMaterial: MeshPhysicalMaterial, - MeshStandardMaterial: MeshStandardMaterial, - MeshPhongMaterial: MeshPhongMaterial, - MeshToonMaterial: MeshToonMaterial, - MeshNormalMaterial: MeshNormalMaterial, - MeshLambertMaterial: MeshLambertMaterial, - MeshDepthMaterial: MeshDepthMaterial, - MeshDistanceMaterial: MeshDistanceMaterial, - MeshBasicMaterial: MeshBasicMaterial, - MeshMatcapMaterial: MeshMatcapMaterial, - LineDashedMaterial: LineDashedMaterial, - LineBasicMaterial: LineBasicMaterial, - Material: Material - }); - - /** - * @author tschw - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ - - var AnimationUtils = { - - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function ( array, from, to ) { - - if ( AnimationUtils.isTypedArray( array ) ) { - - // in ios9 array.subarray(from, undefined) will return empty array - // but array.subarray(from) or array.subarray(from, len) is correct - return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); - - } - - return array.slice( from, to ); - - }, - - // converts an array to a specific type - convertArray: function ( array, type, forceClone ) { - - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) { return array; } - - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - - return new type( array ); // create typed array - - } - - return Array.prototype.slice.call( array ); // create Array - - }, - - isTypedArray: function ( object ) { - - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); - - }, - - // returns an array by which times and values can be sorted - getKeyframeOrder: function ( times ) { - - function compareTime( i, j ) { - - return times[ i ] - times[ j ]; - - } - - var n = times.length; - var result = new Array( n ); - for ( var i = 0; i !== n; ++ i ) { result[ i ] = i; } - - result.sort( compareTime ); - - return result; - - }, - - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function ( values, stride, order ) { - - var nValues = values.length; - var result = new values.constructor( nValues ); - - for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - - var srcOffset = order[ i ] * stride; - - for ( var j = 0; j !== stride; ++ j ) { - - result[ dstOffset ++ ] = values[ srcOffset + j ]; - - } - - } - - return result; - - }, - - // function for parsing AOS keyframe formats - flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { - - var i = 1, key = jsonKeys[ 0 ]; - - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - - key = jsonKeys[ i ++ ]; - - } - - if ( key === undefined ) { return; } // no data - - var value = key[ valuePropertyName ]; - if ( value === undefined ) { return; } // no data - - if ( Array.isArray( value ) ) { - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push.apply( values, value ); // push all elements - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else if ( value.toArray !== undefined ) { - - // ...assume THREE.Math-ish - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - value.toArray( values, values.length ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } else { - - // otherwise push as-is - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - values.push( value ); - - } - - key = jsonKeys[ i ++ ]; - - } while ( key !== undefined ); - - } - - }, - - subclip: function ( sourceClip, name, startFrame, endFrame, fps ) { - - fps = fps || 30; - - var clip = sourceClip.clone(); - - clip.name = name; - - var tracks = []; - - for ( var i = 0; i < clip.tracks.length; ++ i ) { - - var track = clip.tracks[ i ]; - var valueSize = track.getValueSize(); - - var times = []; - var values = []; - - for ( var j = 0; j < track.times.length; ++ j ) { - - var frame = track.times[ j ] * fps; - - if ( frame < startFrame || frame >= endFrame ) { continue; } - - times.push( track.times[ j ] ); - - for ( var k = 0; k < valueSize; ++ k ) { - - values.push( track.values[ j * valueSize + k ] ); - - } - - } - - if ( times.length === 0 ) { continue; } - - track.times = AnimationUtils.convertArray( times, track.times.constructor ); - track.values = AnimationUtils.convertArray( values, track.values.constructor ); - - tracks.push( track ); - - } - - clip.tracks = tracks; - - // find minimum .times value across all tracks in the trimmed clip - - var minStartTime = Infinity; - - for ( var i = 0; i < clip.tracks.length; ++ i ) { - - if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { - - minStartTime = clip.tracks[ i ].times[ 0 ]; - - } - - } - - // shift all tracks such that clip begins at t=0 - - for ( var i = 0; i < clip.tracks.length; ++ i ) { - - clip.tracks[ i ].shift( - 1 * minStartTime ); - - } - - clip.resetDuration(); - - return clip; - - }, - - makeClipAdditive: function ( targetClip, referenceFrame, referenceClip, fps ) { - - if ( referenceFrame === undefined ) { referenceFrame = 0; } - if ( referenceClip === undefined ) { referenceClip = targetClip; } - if ( fps === undefined || fps <= 0 ) { fps = 30; } - - var numTracks = targetClip.tracks.length; - var referenceTime = referenceFrame / fps; - - // Make each track's values relative to the values at the reference frame - for ( var i = 0; i < numTracks; ++ i ) { - - var referenceTrack = referenceClip.tracks[ i ]; - var referenceTrackType = referenceTrack.ValueTypeName; - - // Skip this track if it's non-numeric - if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) { continue; } - - // Find the track in the target clip whose name and type matches the reference track - var targetTrack = targetClip.tracks.find( function ( track ) { - - return track.name === referenceTrack.name - && track.ValueTypeName === referenceTrackType; - - } ); - - if ( targetTrack === undefined ) { continue; } - - var valueSize = referenceTrack.getValueSize(); - var lastIndex = referenceTrack.times.length - 1; - var referenceValue; - - // Find the value to subtract out of the track - if ( referenceTime <= referenceTrack.times[ 0 ] ) { - - // Reference frame is earlier than the first keyframe, so just use the first keyframe - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, 0, referenceTrack.valueSize ); - - } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { - - // Reference frame is after the last keyframe, so just use the last keyframe - var startIndex = lastIndex * valueSize; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex ); - - } else { - - // Interpolate to the reference value - var interpolant = referenceTrack.createInterpolant(); - interpolant.evaluate( referenceTime ); - referenceValue = interpolant.resultBuffer; - - } - - // Conjugate the quaternion - if ( referenceTrackType === 'quaternion' ) { - - var referenceQuat = new Quaternion( - referenceValue[ 0 ], - referenceValue[ 1 ], - referenceValue[ 2 ], - referenceValue[ 3 ] - ).normalize().conjugate(); - referenceQuat.toArray( referenceValue ); - - } - - // Subtract the reference value from all of the track values - - var numTimes = targetTrack.times.length; - for ( var j = 0; j < numTimes; ++ j ) { - - var valueStart = j * valueSize; - - if ( referenceTrackType === 'quaternion' ) { - - // Multiply the conjugate for quaternion track types - Quaternion.multiplyQuaternionsFlat( - targetTrack.values, - valueStart, - referenceValue, - 0, - targetTrack.values, - valueStart - ); - - } else { - - // Subtract each value for all other numeric track types - for ( var k = 0; k < valueSize; ++ k ) { - - targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; - - } - - } - - } - - } - - targetClip.blendMode = AdditiveAnimationBlendMode; - - return targetClip; - - } - - }; - - /** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - * @author tschw - */ - - function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; - - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; - - } - - Object.assign( Interpolant.prototype, { - - evaluate: function ( t ) { - - var pp = this.parameterPositions, - i1 = this._cachedIndex, - - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; - - validate_interval: { - - seek: { - - var right; - - linear_scan: { - - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { - - for ( var giveUpAt = i1 + 2; ; ) { - - if ( t1 === undefined ) { - - if ( t < t0 ) { break forward_scan; } - - // after end - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); - - } - - if ( i1 === giveUpAt ) { break; } // this loop - - t0 = t1; - t1 = pp[ ++ i1 ]; - - if ( t < t1 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; - - } - - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { - - // looping? - - var t1global = pp[ 1 ]; - - if ( t < t1global ) { - - i1 = 2; // + 1, using the scan for the details - t0 = t1global; - - } - - // linear reverse scan - - for ( var giveUpAt = i1 - 2; ; ) { - - if ( t0 === undefined ) { - - // before start - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( i1 === giveUpAt ) { break; } // this loop - - t1 = t0; - t0 = pp[ -- i1 - 1 ]; - - if ( t >= t0 ) { - - // we have arrived at the sought interval - break seek; - - } - - } - - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; - - } - - // the interval is valid - - break validate_interval; - - } // linear scan - - // binary search - - while ( i1 < right ) { - - var mid = ( i1 + right ) >>> 1; - - if ( t < pp[ mid ] ) { - - right = mid; - - } else { - - i1 = mid + 1; - - } - - } - - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; - - // check boundary cases, again - - if ( t0 === undefined ) { - - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); - - } - - if ( t1 === undefined ) { - - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); - - } - - } // seek - - this._cachedIndex = i1; - - this.intervalChanged_( i1, t0, t1 ); - - } // validate_interval - - return this.interpolate_( i1, t0, t, t1 ); - - }, - - settings: null, // optional, subclass-specific settings structure - // Note: The indirection allows central control of many interpolants. - - // --- Protected interface - - DefaultSettings_: {}, - - getSettings_: function () { - - return this.settings || this.DefaultSettings_; - - }, - - copySampleValue_: function ( index ) { - - // copies a sample value to the result buffer - - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; - - for ( var i = 0; i !== stride; ++ i ) { - - result[ i ] = values[ offset + i ]; - - } - - return result; - - }, - - // Template methods for derived classes: - - interpolate_: function ( /* i1, t0, t, t1 */ ) { - - throw new Error( 'call to abstract method' ); - // implementations shall return this.resultBuffer - - }, - - intervalChanged_: function ( /* i1, t0, t1 */ ) { - - // empty - - } - - } ); - - // DECLARE ALIAS AFTER assign prototype - Object.assign( Interpolant.prototype, { - - //( 0, t, t0 ), returns this.resultBuffer - beforeStart_: Interpolant.prototype.copySampleValue_, - - //( N-1, tN-1, t ), returns this.resultBuffer - afterEnd_: Interpolant.prototype.copySampleValue_, - - } ); - - /** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - * - * @author tschw - */ - - function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - this._weightPrev = - 0; - this._offsetPrev = - 0; - this._weightNext = - 0; - this._offsetNext = - 0; - - } - - CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: CubicInterpolant, - - DefaultSettings_: { - - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - - }, - - intervalChanged_: function ( i1, t0, t1 ) { - - var pp = this.parameterPositions, - iPrev = i1 - 2, - iNext = i1 + 1, - - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; - - if ( tPrev === undefined ) { - - switch ( this.getSettings_().endingStart ) { - - case ZeroSlopeEnding: - - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; - - } - - } - - if ( tNext === undefined ) { - - switch ( this.getSettings_().endingEnd ) { - - case ZeroSlopeEnding: - - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; - - break; - - case WrapAroundEnding: - - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; - - break; - - default: // ZeroCurvatureEnding - - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; - - } - - } - - var halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; - - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; - - }, - - interpolate_: function ( i1, t0, t, t1 ) { - - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, - - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; - - // evaluate polynomials - - var sP = - wP * ppp + 2 * wP * pp - wP * p; - var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; - var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - var sN = wN * ppp - wN * pp; - - // combine data linearly - - for ( var i = 0; i !== stride; ++ i ) { - - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; - - } - - return result; - - } - - } ); - - /** - * @author tschw - */ - - function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: LinearInterpolant, - - interpolate_: function ( i1, t0, t, t1 ) { - - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - offset1 = i1 * stride, - offset0 = offset1 - stride, - - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; - - for ( var i = 0; i !== stride; ++ i ) { - - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; - - } - - return result; - - } - - } ); - - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - * - * @author tschw - */ - - function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: DiscreteInterpolant, - - interpolate_: function ( i1 /*, t0, t, t1 */ ) { - - return this.copySampleValue_( i1 - 1 ); - - } - - } ); - - /** - * - * A timed sequence of keyframes for a specific property. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function KeyframeTrack( name, times, values, interpolation ) { - - if ( name === undefined ) { throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); } - if ( times === undefined || times.length === 0 ) { throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); } - - this.name = name; - - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); - - this.setInterpolation( interpolation || this.DefaultInterpolation ); - - } - - // Static methods - - Object.assign( KeyframeTrack, { - - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): - - toJSON: function ( track ) { - - var trackType = track.constructor; - - var json; - - // derived classes can define a static toJSON method - if ( trackType.toJSON !== undefined ) { - - json = trackType.toJSON( track ); - - } else { - - // by default, we assume the data can be serialized as-is - json = { - - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) - - }; - - var interpolation = track.getInterpolation(); - - if ( interpolation !== track.DefaultInterpolation ) { - - json.interpolation = interpolation; - - } - - } - - json.type = track.ValueTypeName; // mandatory - - return json; - - } - - } ); - - Object.assign( KeyframeTrack.prototype, { - - constructor: KeyframeTrack, - - TimeBufferType: Float32Array, - - ValueBufferType: Float32Array, - - DefaultInterpolation: InterpolateLinear, - - InterpolantFactoryMethodDiscrete: function ( result ) { - - return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodLinear: function ( result ) { - - return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodSmooth: function ( result ) { - - return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - setInterpolation: function ( interpolation ) { - - var factoryMethod; - - switch ( interpolation ) { - - case InterpolateDiscrete: - - factoryMethod = this.InterpolantFactoryMethodDiscrete; - - break; - - case InterpolateLinear: - - factoryMethod = this.InterpolantFactoryMethodLinear; - - break; - - case InterpolateSmooth: - - factoryMethod = this.InterpolantFactoryMethodSmooth; - - break; - - } - - if ( factoryMethod === undefined ) { - - var message = "unsupported interpolation for " + - this.ValueTypeName + " keyframe track named " + this.name; - - if ( this.createInterpolant === undefined ) { - - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { - - this.setInterpolation( this.DefaultInterpolation ); - - } else { - - throw new Error( message ); // fatal, in this case - - } - - } - - console.warn( 'THREE.KeyframeTrack:', message ); - return this; - - } - - this.createInterpolant = factoryMethod; - - return this; - - }, - - getInterpolation: function () { - - switch ( this.createInterpolant ) { - - case this.InterpolantFactoryMethodDiscrete: - - return InterpolateDiscrete; - - case this.InterpolantFactoryMethodLinear: - - return InterpolateLinear; - - case this.InterpolantFactoryMethodSmooth: - - return InterpolateSmooth; - - } - - }, - - getValueSize: function () { - - return this.values.length / this.times.length; - - }, - - // move all keyframes either forwards or backwards in time - shift: function ( timeOffset ) { - - if ( timeOffset !== 0.0 ) { - - var times = this.times; - - for ( var i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] += timeOffset; - - } - - } - - return this; - - }, - - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale: function ( timeScale ) { - - if ( timeScale !== 1.0 ) { - - var times = this.times; - - for ( var i = 0, n = times.length; i !== n; ++ i ) { - - times[ i ] *= timeScale; - - } - - } - - return this; - - }, - - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim: function ( startTime, endTime ) { - - var times = this.times, - nKeys = times.length, - from = 0, - to = nKeys - 1; - - while ( from !== nKeys && times[ from ] < startTime ) { - - ++ from; - - } - - while ( to !== - 1 && times[ to ] > endTime ) { - - -- to; - - } - - ++ to; // inclusive -> exclusive bound - - if ( from !== 0 || to !== nKeys ) { - - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) { - - to = Math.max( to, 1 ); - from = to - 1; - - } - - var stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); - - } - - return this; - - }, - - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate: function () { - - var valid = true; - - var valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { - - console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); - valid = false; - - } - - var times = this.times, - values = this.values, - - nKeys = times.length; - - if ( nKeys === 0 ) { - - console.error( 'THREE.KeyframeTrack: Track is empty.', this ); - valid = false; - - } - - var prevTime = null; - - for ( var i = 0; i !== nKeys; i ++ ) { - - var currTime = times[ i ]; - - if ( typeof currTime === 'number' && isNaN( currTime ) ) { - - console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); - valid = false; - break; - - } - - if ( prevTime !== null && prevTime > currTime ) { - - console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); - valid = false; - break; - - } - - prevTime = currTime; - - } - - if ( values !== undefined ) { - - if ( AnimationUtils.isTypedArray( values ) ) { - - for ( var i = 0, n = values.length; i !== n; ++ i ) { - - var value = values[ i ]; - - if ( isNaN( value ) ) { - - console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); - valid = false; - break; - - } - - } - - } - - } - - return valid; - - }, - - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize: function () { - - // times or values may be shared with other tracks, so overwriting is unsafe - var times = AnimationUtils.arraySlice( this.times ), - values = AnimationUtils.arraySlice( this.values ), - stride = this.getValueSize(), - - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - - writeIndex = 1, - lastIndex = times.length - 1; - - for ( var i = 1; i < lastIndex; ++ i ) { - - var keep = false; - - var time = times[ i ]; - var timeNext = times[ i + 1 ]; - - // remove adjacent keyframes scheduled at the same time - - if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { - - if ( ! smoothInterpolation ) { - - // remove unnecessary keyframes same as their neighbors - - var offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; - - for ( var j = 0; j !== stride; ++ j ) { - - var value = values[ offset + j ]; - - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { - - keep = true; - break; - - } - - } - - } else { - - keep = true; - - } - - } - - // in-place compaction - - if ( keep ) { - - if ( i !== writeIndex ) { - - times[ writeIndex ] = times[ i ]; - - var readOffset = i * stride, - writeOffset = writeIndex * stride; - - for ( var j = 0; j !== stride; ++ j ) { - - values[ writeOffset + j ] = values[ readOffset + j ]; - - } - - } - - ++ writeIndex; - - } - - } - - // flush last keyframe (compaction looks ahead) - - if ( lastIndex > 0 ) { - - times[ writeIndex ] = times[ lastIndex ]; - - for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { - - values[ writeOffset + j ] = values[ readOffset + j ]; - - } - - ++ writeIndex; - - } - - if ( writeIndex !== times.length ) { - - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); - - } else { - - this.times = times; - this.values = values; - - } - - return this; - - }, - - clone: function () { - - var times = AnimationUtils.arraySlice( this.times, 0 ); - var values = AnimationUtils.arraySlice( this.values, 0 ); - - var TypedKeyframeTrack = this.constructor; - var track = new TypedKeyframeTrack( this.name, times, values ); - - // Interpolant argument to constructor is not saved, so copy the factory method directly. - track.createInterpolant = this.createInterpolant; - - return track; - - } - - } ); - - /** - * - * A Track of Boolean keyframe values. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function BooleanKeyframeTrack( name, times, values ) { - - KeyframeTrack.call( this, name, times, values ); - - } - - BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: BooleanKeyframeTrack, - - ValueTypeName: 'bool', - ValueBufferType: Array, - - DefaultInterpolation: InterpolateDiscrete, - - InterpolantFactoryMethodLinear: undefined, - InterpolantFactoryMethodSmooth: undefined - - // Note: Actually this track could have a optimized / compressed - // representation of a single value and a custom interpolant that - // computes "firstValue ^ isOdd( index )". - - } ); - - /** - * - * A Track of keyframe values that represent color. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function ColorKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: ColorKeyframeTrack, - - ValueTypeName: 'color' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - // Note: Very basic implementation and nothing special yet. - // However, this is the place for color space parameterization. - - } ); - - /** - * - * A Track of numeric keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function NumberKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: NumberKeyframeTrack, - - ValueTypeName: 'number' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - } ); - - /** - * Spherical linear unit quaternion interpolant. - * - * @author tschw - */ - - function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - - } - - QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: QuaternionLinearInterpolant, - - interpolate_: function ( i1, t0, t, t1 ) { - - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - - offset = i1 * stride, - - alpha = ( t - t0 ) / ( t1 - t0 ); - - for ( var end = offset + stride; offset !== end; offset += 4 ) { - - Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); - - } - - return result; - - } - - } ); - - /** - * - * A Track of quaternion keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function QuaternionKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: QuaternionKeyframeTrack, - - ValueTypeName: 'quaternion', - - // ValueBufferType is inherited - - DefaultInterpolation: InterpolateLinear, - - InterpolantFactoryMethodLinear: function ( result ) { - - return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); - - }, - - InterpolantFactoryMethodSmooth: undefined // not yet implemented - - } ); - - /** - * - * A Track that interpolates Strings - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function StringKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: StringKeyframeTrack, - - ValueTypeName: 'string', - ValueBufferType: Array, - - DefaultInterpolation: InterpolateDiscrete, - - InterpolantFactoryMethodLinear: undefined, - - InterpolantFactoryMethodSmooth: undefined - - } ); - - /** - * - * A Track of vectored keyframe values. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function VectorKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrack.call( this, name, times, values, interpolation ); - - } - - VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - - constructor: VectorKeyframeTrack, - - ValueTypeName: 'vector' - - // ValueBufferType is inherited - - // DefaultInterpolation is inherited - - } ); - - /** - * - * Reusable set of Tracks that represent an animation. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ - - function AnimationClip( name, duration, tracks, blendMode ) { - - this.name = name; - this.tracks = tracks; - this.duration = ( duration !== undefined ) ? duration : - 1; - this.blendMode = ( blendMode !== undefined ) ? blendMode : NormalAnimationBlendMode; - - this.uuid = MathUtils.generateUUID(); - - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { - - this.resetDuration(); - - } - - } - - function getTrackTypeForValueTypeName( typeName ) { - - switch ( typeName.toLowerCase() ) { - - case 'scalar': - case 'double': - case 'float': - case 'number': - case 'integer': - - return NumberKeyframeTrack; - - case 'vector': - case 'vector2': - case 'vector3': - case 'vector4': - - return VectorKeyframeTrack; - - case 'color': - - return ColorKeyframeTrack; - - case 'quaternion': - - return QuaternionKeyframeTrack; - - case 'bool': - case 'boolean': - - return BooleanKeyframeTrack; - - case 'string': - - return StringKeyframeTrack; - - } - - throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); - - } - - function parseKeyframeTrack( json ) { - - if ( json.type === undefined ) { - - throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); - - } - - var trackType = getTrackTypeForValueTypeName( json.type ); - - if ( json.times === undefined ) { - - var times = [], values = []; - - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - - json.times = times; - json.values = values; - - } - - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { - - return trackType.parse( json ); - - } else { - - // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); - - } - - } - - Object.assign( AnimationClip, { - - parse: function ( json ) { - - var tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); - - for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { - - tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); - - } - - return new AnimationClip( json.name, json.duration, tracks, json.blendMode ); - - }, - - toJSON: function ( clip ) { - - var tracks = [], - clipTracks = clip.tracks; - - var json = { - - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks, - 'uuid': clip.uuid, - 'blendMode': clip.blendMode - - }; - - for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { - - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); - - } - - return json; - - }, - - CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { - - var numMorphTargets = morphTargetSequence.length; - var tracks = []; - - for ( var i = 0; i < numMorphTargets; i ++ ) { - - var times = []; - var values = []; - - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); - - values.push( 0, 1, 0 ); - - var order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); - - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { - - times.push( numMorphTargets ); - values.push( values[ 0 ] ); - - } - - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); - - } - - return new AnimationClip( name, - 1, tracks ); - - }, - - findByName: function ( objectOrClipArray, name ) { - - var clipArray = objectOrClipArray; - - if ( ! Array.isArray( objectOrClipArray ) ) { - - var o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; - - } - - for ( var i = 0; i < clipArray.length; i ++ ) { - - if ( clipArray[ i ].name === name ) { - - return clipArray[ i ]; - - } - - } - - return null; - - }, - - CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { - - var animationToMorphTargets = {}; - - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - var pattern = /^([\w-]*?)([\d]+)$/; - - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { - - var morphTarget = morphTargets[ i ]; - var parts = morphTarget.name.match( pattern ); - - if ( parts && parts.length > 1 ) { - - var name = parts[ 1 ]; - - var animationMorphTargets = animationToMorphTargets[ name ]; - if ( ! animationMorphTargets ) { - - animationToMorphTargets[ name ] = animationMorphTargets = []; - - } - - animationMorphTargets.push( morphTarget ); - - } - - } - - var clips = []; - - for ( var name in animationToMorphTargets ) { - - clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - - } - - return clips; - - }, - - // parse the animation.hierarchy format - parseAnimation: function ( animation, bones ) { - - if ( ! animation ) { - - console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); - return null; - - } - - var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { - - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { - - var times = []; - var values = []; - - AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); - - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { - - destTracks.push( new trackType( trackName, times, values ) ); - - } - - } - - }; - - var tracks = []; - - var clipName = animation.name || 'default'; - // automatic length determination in AnimationClip. - var duration = animation.length || - 1; - var fps = animation.fps || 30; - var blendMode = animation.blendMode; - - var hierarchyTracks = animation.hierarchy || []; - - for ( var h = 0; h < hierarchyTracks.length; h ++ ) { - - var animationKeys = hierarchyTracks[ h ].keys; - - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) { continue; } - - // process morph targets - if ( animationKeys[ 0 ].morphTargets ) { - - // figure out all morph targets used in this track - var morphTargetNames = {}; - - for ( var k = 0; k < animationKeys.length; k ++ ) { - - if ( animationKeys[ k ].morphTargets ) { - - for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { - - morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; - - } - - } - - } - - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( var morphTargetName in morphTargetNames ) { - - var times = []; - var values = []; - - for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { - - var animationKey = animationKeys[ k ]; - - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - - } - - tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - - } - - duration = morphTargetNames.length * ( fps || 1.0 ); - - } else { - - // ...assume skeletal animation - - var boneName = '.bones[' + bones[ h ].name + ']'; - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); - - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); - - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); - - } - - } - - if ( tracks.length === 0 ) { - - return null; - - } - - var clip = new AnimationClip( clipName, duration, tracks, blendMode ); - - return clip; - - } - - } ); - - Object.assign( AnimationClip.prototype, { - - resetDuration: function () { - - var tracks = this.tracks, duration = 0; - - for ( var i = 0, n = tracks.length; i !== n; ++ i ) { - - var track = this.tracks[ i ]; - - duration = Math.max( duration, track.times[ track.times.length - 1 ] ); - - } - - this.duration = duration; - - return this; - - }, - - trim: function () { - - for ( var i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].trim( 0, this.duration ); - - } - - return this; - - }, - - validate: function () { - - var valid = true; - - for ( var i = 0; i < this.tracks.length; i ++ ) { - - valid = valid && this.tracks[ i ].validate(); - - } - - return valid; - - }, - - optimize: function () { - - for ( var i = 0; i < this.tracks.length; i ++ ) { - - this.tracks[ i ].optimize(); - - } - - return this; - - }, - - clone: function () { - - var tracks = []; - - for ( var i = 0; i < this.tracks.length; i ++ ) { - - tracks.push( this.tracks[ i ].clone() ); - - } - - return new AnimationClip( this.name, this.duration, tracks, this.blendMode ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var Cache = { - - enabled: false, - - files: {}, - - add: function ( key, file ) { - - if ( this.enabled === false ) { return; } - - // console.log( 'THREE.Cache', 'Adding key:', key ); - - this.files[ key ] = file; - - }, - - get: function ( key ) { - - if ( this.enabled === false ) { return; } - - // console.log( 'THREE.Cache', 'Checking key:', key ); - - return this.files[ key ]; - - }, - - remove: function ( key ) { - - delete this.files[ key ]; - - }, - - clear: function () { - - this.files = {}; - - } - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function LoadingManager( onLoad, onProgress, onError ) { - - var scope = this; - - var isLoading = false; - var itemsLoaded = 0; - var itemsTotal = 0; - var urlModifier = undefined; - var handlers = []; - - // Refer to #5689 for the reason why we don't set .onStart - // in the constructor - - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; - - this.itemStart = function ( url ) { - - itemsTotal ++; - - if ( isLoading === false ) { - - if ( scope.onStart !== undefined ) { - - scope.onStart( url, itemsLoaded, itemsTotal ); - - } - - } - - isLoading = true; - - }; - - this.itemEnd = function ( url ) { - - itemsLoaded ++; - - if ( scope.onProgress !== undefined ) { - - scope.onProgress( url, itemsLoaded, itemsTotal ); - - } - - if ( itemsLoaded === itemsTotal ) { - - isLoading = false; - - if ( scope.onLoad !== undefined ) { - - scope.onLoad(); - - } - - } - - }; - - this.itemError = function ( url ) { - - if ( scope.onError !== undefined ) { - - scope.onError( url ); - - } - - }; - - this.resolveURL = function ( url ) { - - if ( urlModifier ) { - - return urlModifier( url ); - - } - - return url; - - }; - - this.setURLModifier = function ( transform ) { - - urlModifier = transform; - - return this; - - }; - - this.addHandler = function ( regex, loader ) { - - handlers.push( regex, loader ); - - return this; - - }; - - this.removeHandler = function ( regex ) { - - var index = handlers.indexOf( regex ); - - if ( index !== - 1 ) { - - handlers.splice( index, 2 ); - - } - - return this; - - }; - - this.getHandler = function ( file ) { - - for ( var i = 0, l = handlers.length; i < l; i += 2 ) { - - var regex = handlers[ i ]; - var loader = handlers[ i + 1 ]; - - if ( regex.global ) { regex.lastIndex = 0; } // see #17920 - - if ( regex.test( file ) ) { - - return loader; - - } - - } - - return null; - - }; - - } - - var DefaultLoadingManager = new LoadingManager(); - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function Loader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - - this.crossOrigin = 'anonymous'; - this.path = ''; - this.resourcePath = ''; - this.requestHeader = {}; - - } - - Object.assign( Loader.prototype, { - - load: function ( /* url, onLoad, onProgress, onError */ ) {}, - - loadAsync: function ( url, onProgress ) { - - var scope = this; - - return new Promise( function ( resolve, reject ) { - - scope.load( url, resolve, onProgress, reject ); - - } ); - - }, - - parse: function ( /* data */ ) {}, - - setCrossOrigin: function ( crossOrigin ) { - - this.crossOrigin = crossOrigin; - return this; - - }, - - setPath: function ( path ) { - - this.path = path; - return this; - - }, - - setResourcePath: function ( resourcePath ) { - - this.resourcePath = resourcePath; - return this; - - }, - - setRequestHeader: function ( requestHeader ) { - - this.requestHeader = requestHeader; - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var loading = {}; - - function FileLoader( manager ) { - - Loader.call( this, manager ); - - } - - FileLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: FileLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - if ( url === undefined ) { url = ''; } - - if ( this.path !== undefined ) { url = this.path + url; } - - url = this.manager.resolveURL( url ); - - var scope = this; - - var cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) { onLoad( cached ); } - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - // Check if request is duplicate - - if ( loading[ url ] !== undefined ) { - - loading[ url ].push( { - - onLoad: onLoad, - onProgress: onProgress, - onError: onError - - } ); - - return; - - } - - // Check for data: URI - var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - var dataUriRegexResult = url.match( dataUriRegex ); - - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { - - var mimeType = dataUriRegexResult[ 1 ]; - var isBase64 = !! dataUriRegexResult[ 2 ]; - var data = dataUriRegexResult[ 3 ]; - - data = decodeURIComponent( data ); - - if ( isBase64 ) { data = atob( data ); } - - try { - - var response; - var responseType = ( this.responseType || '' ).toLowerCase(); - - switch ( responseType ) { - - case 'arraybuffer': - case 'blob': - - var view = new Uint8Array( data.length ); - - for ( var i = 0; i < data.length; i ++ ) { - - view[ i ] = data.charCodeAt( i ); - - } - - if ( responseType === 'blob' ) { - - response = new Blob( [ view.buffer ], { type: mimeType } ); - - } else { - - response = view.buffer; - - } - - break; - - case 'document': - - var parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); - - break; - - case 'json': - - response = JSON.parse( data ); - - break; - - default: // 'text' or other - - response = data; - - break; - - } - - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { - - if ( onLoad ) { onLoad( response ); } - - scope.manager.itemEnd( url ); - - }, 0 ); - - } catch ( error ) { - - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { - - if ( onError ) { onError( error ); } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, 0 ); - - } - - } else { - - // Initialise array for duplicate requests - - loading[ url ] = []; - - loading[ url ].push( { - - onLoad: onLoad, - onProgress: onProgress, - onError: onError - - } ); - - var request = new XMLHttpRequest(); - - request.open( 'GET', url, true ); - - request.addEventListener( 'load', function ( event ) { - - var response = this.response; - - var callbacks = loading[ url ]; - - delete loading[ url ]; - - if ( this.status === 200 || this.status === 0 ) { - - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. - - if ( this.status === 0 ) { console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); } - - // Add to cache only on HTTP success, so that we do not cache - // error response bodies as proper responses to requests. - Cache.add( url, response ); - - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - - var callback = callbacks[ i ]; - if ( callback.onLoad ) { callback.onLoad( response ); } - - } - - scope.manager.itemEnd( url ); - - } else { - - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - - var callback = callbacks[ i ]; - if ( callback.onError ) { callback.onError( event ); } - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } - - }, false ); - - request.addEventListener( 'progress', function ( event ) { - - var callbacks = loading[ url ]; - - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - - var callback = callbacks[ i ]; - if ( callback.onProgress ) { callback.onProgress( event ); } - - } - - }, false ); - - request.addEventListener( 'error', function ( event ) { - - var callbacks = loading[ url ]; - - delete loading[ url ]; - - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - - var callback = callbacks[ i ]; - if ( callback.onError ) { callback.onError( event ); } - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, false ); - - request.addEventListener( 'abort', function ( event ) { - - var callbacks = loading[ url ]; - - delete loading[ url ]; - - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - - var callback = callbacks[ i ]; - if ( callback.onError ) { callback.onError( event ); } - - } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, false ); - - if ( this.responseType !== undefined ) { request.responseType = this.responseType; } - if ( this.withCredentials !== undefined ) { request.withCredentials = this.withCredentials; } - - if ( request.overrideMimeType ) { request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); } - - for ( var header in this.requestHeader ) { - - request.setRequestHeader( header, this.requestHeader[ header ] ); - - } - - request.send( null ); - - } - - scope.manager.itemStart( url ); - - return request; - - }, - - setResponseType: function ( value ) { - - this.responseType = value; - return this; - - }, - - setWithCredentials: function ( value ) { - - this.withCredentials = value; - return this; - - }, - - setMimeType: function ( value ) { - - this.mimeType = value; - return this; - - } - - } ); - - /** - * @author bhouston / http://clara.io/ - */ - - function AnimationLoader( manager ) { - - Loader.call( this, manager ); - - } - - AnimationLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: AnimationLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - var animations = []; - - for ( var i = 0; i < json.length; i ++ ) { - - var clip = AnimationClip.parse( json[ i ] ); - - animations.push( clip ); - - } - - return animations; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * - * Abstract Base class to block based textures loader (dds, pvr, ...) - * - * Sub classes have to implement the parse() method which will be used in load(). - */ - - function CompressedTextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: CompressedTextureLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var images = []; - - var texture = new CompressedTexture(); - texture.image = images; - - var loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - - function loadTexture( i ) { - - loader.load( url[ i ], function ( buffer ) { - - var texDatas = scope.parse( buffer, true ); - - images[ i ] = { - width: texDatas.width, - height: texDatas.height, - format: texDatas.format, - mipmaps: texDatas.mipmaps - }; - - loaded += 1; - - if ( loaded === 6 ) { - - if ( texDatas.mipmapCount === 1 ) - { texture.minFilter = LinearFilter; } - - texture.format = texDatas.format; - texture.needsUpdate = true; - - if ( onLoad ) { onLoad( texture ); } - - } - - }, onProgress, onError ); - - } - - if ( Array.isArray( url ) ) { - - var loaded = 0; - - for ( var i = 0, il = url.length; i < il; ++ i ) { - - loadTexture( i ); - - } - - } else { - - // compressed cubemap texture stored in a single DDS file - - loader.load( url, function ( buffer ) { - - var texDatas = scope.parse( buffer, true ); - - if ( texDatas.isCubemap ) { - - var faces = texDatas.mipmaps.length / texDatas.mipmapCount; - - for ( var f = 0; f < faces; f ++ ) { - - images[ f ] = { mipmaps: [] }; - - for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { - - images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); - images[ f ].format = texDatas.format; - images[ f ].width = texDatas.width; - images[ f ].height = texDatas.height; - - } - - } - - } else { - - texture.image.width = texDatas.width; - texture.image.height = texDatas.height; - texture.mipmaps = texDatas.mipmaps; - - } - - if ( texDatas.mipmapCount === 1 ) { - - texture.minFilter = LinearFilter; - - } - - texture.format = texDatas.format; - texture.needsUpdate = true; - - if ( onLoad ) { onLoad( texture ); } - - }, onProgress, onError ); - - } - - return texture; - - } - - } ); - - /** - * @author Nikos M. / https://github.com/foo123/ - * - * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) - * - * Sub classes have to implement the parse() method which will be used in load(). - */ - - function DataTextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - DataTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: DataTextureLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var texture = new DataTexture(); - - var loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setPath( this.path ); - loader.load( url, function ( buffer ) { - - var texData = scope.parse( buffer ); - - if ( ! texData ) { return; } - - if ( texData.image !== undefined ) { - - texture.image = texData.image; - - } else if ( texData.data !== undefined ) { - - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; - - } - - texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; - texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; - - texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; - texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; - - texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; - - if ( texData.format !== undefined ) { - - texture.format = texData.format; - - } - - if ( texData.type !== undefined ) { - - texture.type = texData.type; - - } - - if ( texData.mipmaps !== undefined ) { - - texture.mipmaps = texData.mipmaps; - texture.minFilter = LinearMipmapLinearFilter; // presumably... - - } - - if ( texData.mipmapCount === 1 ) { - - texture.minFilter = LinearFilter; - - } - - texture.needsUpdate = true; - - if ( onLoad ) { onLoad( texture, texData ); } - - }, onProgress, onError ); - - - return texture; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function ImageLoader( manager ) { - - Loader.call( this, manager ); - - } - - ImageLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: ImageLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - if ( this.path !== undefined ) { url = this.path + url; } - - url = this.manager.resolveURL( url ); - - var scope = this; - - var cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) { onLoad( cached ); } - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); - - function onImageLoad() { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); - - Cache.add( url, this ); - - if ( onLoad ) { onLoad( this ); } - - scope.manager.itemEnd( url ); - - } - - function onImageError( event ) { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); - - if ( onError ) { onError( event ); } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } - - image.addEventListener( 'load', onImageLoad, false ); - image.addEventListener( 'error', onImageError, false ); - - if ( url.substr( 0, 5 ) !== 'data:' ) { - - if ( this.crossOrigin !== undefined ) { image.crossOrigin = this.crossOrigin; } - - } - - scope.manager.itemStart( url ); - - image.src = url; - - return image; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - - function CubeTextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - CubeTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: CubeTextureLoader, - - load: function ( urls, onLoad, onProgress, onError ) { - - var texture = new CubeTexture(); - - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); - - var loaded = 0; - - function loadTexture( i ) { - - loader.load( urls[ i ], function ( image ) { - - texture.images[ i ] = image; - - loaded ++; - - if ( loaded === 6 ) { - - texture.needsUpdate = true; - - if ( onLoad ) { onLoad( texture ); } - - } - - }, undefined, onError ); - - } - - for ( var i = 0; i < urls.length; ++ i ) { - - loadTexture( i ); - - } - - return texture; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function TextureLoader( manager ) { - - Loader.call( this, manager ); - - } - - TextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: TextureLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var texture = new Texture(); - - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); - - loader.load( url, function ( image ) { - - texture.image = image; - - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - var isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; - - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; - - if ( onLoad !== undefined ) { - - onLoad( texture ); - - } - - }, onProgress, onError ); - - return texture; - - } - - } ); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Extensible curve object - * - * Some common of curve methods: - * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) - * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() - * - * This following curves inherit from THREE.Curve: - * - * -- 2D curves -- - * THREE.ArcCurve - * THREE.CubicBezierCurve - * THREE.EllipseCurve - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.SplineCurve - * - * -- 3D curves -- - * THREE.CatmullRomCurve3 - * THREE.CubicBezierCurve3 - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 - * - * A series of curves can be represented as a THREE.CurvePath. - * - **/ - - /************************************************************** - * Abstract Curve base class - **************************************************************/ - - function Curve() { - - this.type = 'Curve'; - - this.arcLengthDivisions = 200; - - } - - Object.assign( Curve.prototype, { - - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] - - getPoint: function ( /* t, optionalTarget */ ) { - - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); - return null; - - }, - - // Get point at relative position in curve according to arc length - // - u [0 .. 1] - - getPointAt: function ( u, optionalTarget ) { - - var t = this.getUtoTmapping( u ); - return this.getPoint( t, optionalTarget ); - - }, - - // Get sequence of points using getPoint( t ) - - getPoints: function ( divisions ) { - - if ( divisions === undefined ) { divisions = 5; } - - var points = []; - - for ( var d = 0; d <= divisions; d ++ ) { - - points.push( this.getPoint( d / divisions ) ); - - } - - return points; - - }, - - // Get sequence of points using getPointAt( u ) - - getSpacedPoints: function ( divisions ) { - - if ( divisions === undefined ) { divisions = 5; } - - var points = []; - - for ( var d = 0; d <= divisions; d ++ ) { - - points.push( this.getPointAt( d / divisions ) ); - - } - - return points; - - }, - - // Get total curve arc length - - getLength: function () { - - var lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; - - }, - - // Get list of cumulative segment lengths - - getLengths: function ( divisions ) { - - if ( divisions === undefined ) { divisions = this.arcLengthDivisions; } - - if ( this.cacheArcLengths && - ( this.cacheArcLengths.length === divisions + 1 ) && - ! this.needsUpdate ) { - - return this.cacheArcLengths; - - } - - this.needsUpdate = false; - - var cache = []; - var current, last = this.getPoint( 0 ); - var p, sum = 0; - - cache.push( 0 ); - - for ( p = 1; p <= divisions; p ++ ) { - - current = this.getPoint( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; - - } - - this.cacheArcLengths = cache; - - return cache; // { sums: cache, sum: sum }; Sum is in the last element. - - }, - - updateArcLengths: function () { - - this.needsUpdate = true; - this.getLengths(); - - }, - - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - - getUtoTmapping: function ( u, distance ) { - - var arcLengths = this.getLengths(); - - var i = 0, il = arcLengths.length; - - var targetArcLength; // The targeted u distance value to get - - if ( distance ) { - - targetArcLength = distance; - - } else { - - targetArcLength = u * arcLengths[ il - 1 ]; - - } - - // binary search for the index with largest value smaller than target u distance - - var low = 0, high = il - 1, comparison; - - while ( low <= high ) { - - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - - comparison = arcLengths[ i ] - targetArcLength; - - if ( comparison < 0 ) { - - low = i + 1; - - } else if ( comparison > 0 ) { - - high = i - 1; - - } else { - - high = i; - break; - - // DONE - - } - - } - - i = high; - - if ( arcLengths[ i ] === targetArcLength ) { - - return i / ( il - 1 ); - - } - - // we could get finer grain at lengths, or use simple interpolation between two points - - var lengthBefore = arcLengths[ i ]; - var lengthAfter = arcLengths[ i + 1 ]; - - var segmentLength = lengthAfter - lengthBefore; - - // determine where we are between the 'before' and 'after' points - - var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - - // add that fractional amount to t - - var t = ( i + segmentFraction ) / ( il - 1 ); - - return t; - - }, - - // Returns a unit vector tangent at t - // In case any sub curve does not implement its tangent derivation, - // 2 points a small delta apart will be used to find its gradient - // which seems to give a reasonable approximation - - getTangent: function ( t, optionalTarget ) { - - var delta = 0.0001; - var t1 = t - delta; - var t2 = t + delta; - - // Capping in case of danger - - if ( t1 < 0 ) { t1 = 0; } - if ( t2 > 1 ) { t2 = 1; } - - var pt1 = this.getPoint( t1 ); - var pt2 = this.getPoint( t2 ); - - var tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); - - tangent.copy( pt2 ).sub( pt1 ).normalize(); - - return tangent; - - }, - - getTangentAt: function ( u, optionalTarget ) { - - var t = this.getUtoTmapping( u ); - return this.getTangent( t, optionalTarget ); - - }, - - computeFrenetFrames: function ( segments, closed ) { - - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - - var normal = new Vector3(); - - var tangents = []; - var normals = []; - var binormals = []; - - var vec = new Vector3(); - var mat = new Matrix4(); - - var i, u, theta; - - // compute the tangent vectors for each segment on the curve - - for ( i = 0; i <= segments; i ++ ) { - - u = i / segments; - - tangents[ i ] = this.getTangentAt( u, new Vector3() ); - tangents[ i ].normalize(); - - } - - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component - - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - var min = Number.MAX_VALUE; - var tx = Math.abs( tangents[ 0 ].x ); - var ty = Math.abs( tangents[ 0 ].y ); - var tz = Math.abs( tangents[ 0 ].z ); - - if ( tx <= min ) { - - min = tx; - normal.set( 1, 0, 0 ); - - } - - if ( ty <= min ) { - - min = ty; - normal.set( 0, 1, 0 ); - - } - - if ( tz <= min ) { - - normal.set( 0, 0, 1 ); - - } - - vec.crossVectors( tangents[ 0 ], normal ).normalize(); - - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - - - // compute the slowly-varying normal and binormal vectors for each segment on the curve - - for ( i = 1; i <= segments; i ++ ) { - - normals[ i ] = normals[ i - 1 ].clone(); - - binormals[ i ] = binormals[ i - 1 ].clone(); - - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - - if ( vec.length() > Number.EPSILON ) { - - vec.normalize(); - - theta = Math.acos( MathUtils.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - - } - - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - - if ( closed === true ) { - - theta = Math.acos( MathUtils.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; - - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - - theta = - theta; - - } - - for ( i = 1; i <= segments; i ++ ) { - - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - - } - - } - - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.arcLengthDivisions = source.arcLengthDivisions; - - return this; - - }, - - toJSON: function () { - - var data = { - metadata: { - version: 4.5, - type: 'Curve', - generator: 'Curve.toJSON' - } - }; - - data.arcLengthDivisions = this.arcLengthDivisions; - data.type = this.type; - - return data; - - }, - - fromJSON: function ( json ) { - - this.arcLengthDivisions = json.arcLengthDivisions; - - return this; - - } - - } ); - - function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - Curve.call( this ); - - this.type = 'EllipseCurve'; - - this.aX = aX || 0; - this.aY = aY || 0; - - this.xRadius = xRadius || 1; - this.yRadius = yRadius || 1; - - this.aStartAngle = aStartAngle || 0; - this.aEndAngle = aEndAngle || 2 * Math.PI; - - this.aClockwise = aClockwise || false; - - this.aRotation = aRotation || 0; - - } - - EllipseCurve.prototype = Object.create( Curve.prototype ); - EllipseCurve.prototype.constructor = EllipseCurve; - - EllipseCurve.prototype.isEllipseCurve = true; - - EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector2(); - - var twoPi = Math.PI * 2; - var deltaAngle = this.aEndAngle - this.aStartAngle; - var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) { deltaAngle += twoPi; } - while ( deltaAngle > twoPi ) { deltaAngle -= twoPi; } - - if ( deltaAngle < Number.EPSILON ) { - - if ( samePoints ) { - - deltaAngle = 0; - - } else { - - deltaAngle = twoPi; - - } - - } - - if ( this.aClockwise === true && ! samePoints ) { - - if ( deltaAngle === twoPi ) { - - deltaAngle = - twoPi; - - } else { - - deltaAngle = deltaAngle - twoPi; - - } - - } - - var angle = this.aStartAngle + t * deltaAngle; - var x = this.aX + this.xRadius * Math.cos( angle ); - var y = this.aY + this.yRadius * Math.sin( angle ); - - if ( this.aRotation !== 0 ) { - - var cos = Math.cos( this.aRotation ); - var sin = Math.sin( this.aRotation ); - - var tx = x - this.aX; - var ty = y - this.aY; - - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; - - } - - return point.set( x, y ); - - }; - - EllipseCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.aX = source.aX; - this.aY = source.aY; - - this.xRadius = source.xRadius; - this.yRadius = source.yRadius; - - this.aStartAngle = source.aStartAngle; - this.aEndAngle = source.aEndAngle; - - this.aClockwise = source.aClockwise; - - this.aRotation = source.aRotation; - - return this; - - }; - - - EllipseCurve.prototype.toJSON = function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.aX = this.aX; - data.aY = this.aY; - - data.xRadius = this.xRadius; - data.yRadius = this.yRadius; - - data.aStartAngle = this.aStartAngle; - data.aEndAngle = this.aEndAngle; - - data.aClockwise = this.aClockwise; - - data.aRotation = this.aRotation; - - return data; - - }; - - EllipseCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.aX = json.aX; - this.aY = json.aY; - - this.xRadius = json.xRadius; - this.yRadius = json.yRadius; - - this.aStartAngle = json.aStartAngle; - this.aEndAngle = json.aEndAngle; - - this.aClockwise = json.aClockwise; - - this.aRotation = json.aRotation; - - return this; - - }; - - function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - this.type = 'ArcCurve'; - - } - - ArcCurve.prototype = Object.create( EllipseCurve.prototype ); - ArcCurve.prototype.constructor = ArcCurve; - - ArcCurve.prototype.isArcCurve = true; - - /** - * @author zz85 https://github.com/zz85 - * - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf - * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 - */ - - - /* - Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM - - This CubicPoly class could be used for reusing some variables and calculations, - but for three.js curve use, it could be possible inlined and flatten into a single function call - which can be placed in CurveUtils. - */ - - function CubicPoly() { - - var c0 = 0, c1 = 0, c2 = 0, c3 = 0; - - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - function init( x0, x1, t0, t1 ) { - - c0 = x0; - c1 = t0; - c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - c3 = 2 * x0 - 2 * x1 + t0 + t1; - - } - - return { - - initCatmullRom: function ( x0, x1, x2, x3, tension ) { - - init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - - }, - - initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { - - // compute tangents when parameterized in [t1,t2] - var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; - - init( x1, x2, t1, t2 ); - - }, - - calc: function ( t ) { - - var t2 = t * t; - var t3 = t2 * t; - return c0 + c1 * t + c2 * t2 + c3 * t3; - - } - - }; - - } - - // - - var tmp = new Vector3(); - var px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); - - function CatmullRomCurve3( points, closed, curveType, tension ) { - - Curve.call( this ); - - this.type = 'CatmullRomCurve3'; - - this.points = points || []; - this.closed = closed || false; - this.curveType = curveType || 'centripetal'; - this.tension = tension || 0.5; - - } - - CatmullRomCurve3.prototype = Object.create( Curve.prototype ); - CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; - - CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; - - CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector3(); - - var points = this.points; - var l = points.length; - - var p = ( l - ( this.closed ? 0 : 1 ) ) * t; - var intPoint = Math.floor( p ); - var weight = p - intPoint; - - if ( this.closed ) { - - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; - - } else if ( weight === 0 && intPoint === l - 1 ) { - - intPoint = l - 2; - weight = 1; - - } - - var p0, p1, p2, p3; // 4 points - - if ( this.closed || intPoint > 0 ) { - - p0 = points[ ( intPoint - 1 ) % l ]; - - } else { - - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; - - } - - p1 = points[ intPoint % l ]; - p2 = points[ ( intPoint + 1 ) % l ]; - - if ( this.closed || intPoint + 2 < l ) { - - p3 = points[ ( intPoint + 2 ) % l ]; - - } else { - - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; - - } - - if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - - // init Centripetal / Chordal Catmull-Rom - var pow = this.curveType === 'chordal' ? 0.5 : 0.25; - var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - - // safety check for repeated points - if ( dt1 < 1e-4 ) { dt1 = 1.0; } - if ( dt0 < 1e-4 ) { dt0 = dt1; } - if ( dt2 < 1e-4 ) { dt2 = dt1; } - - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); - - } else if ( this.curveType === 'catmullrom' ) { - - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); - - } - - point.set( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); - - return point; - - }; - - CatmullRomCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.points = []; - - for ( var i = 0, l = source.points.length; i < l; i ++ ) { - - var point = source.points[ i ]; - - this.points.push( point.clone() ); - - } - - this.closed = source.closed; - this.curveType = source.curveType; - this.tension = source.tension; - - return this; - - }; - - CatmullRomCurve3.prototype.toJSON = function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.points = []; - - for ( var i = 0, l = this.points.length; i < l; i ++ ) { - - var point = this.points[ i ]; - data.points.push( point.toArray() ); - - } - - data.closed = this.closed; - data.curveType = this.curveType; - data.tension = this.tension; - - return data; - - }; - - CatmullRomCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.points = []; - - for ( var i = 0, l = json.points.length; i < l; i ++ ) { - - var point = json.points[ i ]; - this.points.push( new Vector3().fromArray( point ) ); - - } - - this.closed = json.closed; - this.curveType = json.curveType; - this.tension = json.tension; - - return this; - - }; - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - * Bezier Curves formulas obtained from - * http://en.wikipedia.org/wiki/Bézier_curve - */ - - function CatmullRom( t, p0, p1, p2, p3 ) { - - var v0 = ( p2 - p0 ) * 0.5; - var v1 = ( p3 - p1 ) * 0.5; - var t2 = t * t; - var t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; - - } - - // - - function QuadraticBezierP0( t, p ) { - - var k = 1 - t; - return k * k * p; - - } - - function QuadraticBezierP1( t, p ) { - - return 2 * ( 1 - t ) * t * p; - - } - - function QuadraticBezierP2( t, p ) { - - return t * t * p; - - } - - function QuadraticBezier( t, p0, p1, p2 ) { - - return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + - QuadraticBezierP2( t, p2 ); - - } - - // - - function CubicBezierP0( t, p ) { - - var k = 1 - t; - return k * k * k * p; - - } - - function CubicBezierP1( t, p ) { - - var k = 1 - t; - return 3 * k * k * t * p; - - } - - function CubicBezierP2( t, p ) { - - return 3 * ( 1 - t ) * t * t * p; - - } - - function CubicBezierP3( t, p ) { - - return t * t * t * p; - - } - - function CubicBezier( t, p0, p1, p2, p3 ) { - - return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + - CubicBezierP3( t, p3 ); - - } - - function CubicBezierCurve( v0, v1, v2, v3 ) { - - Curve.call( this ); - - this.type = 'CubicBezierCurve'; - - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - this.v3 = v3 || new Vector2(); - - } - - CubicBezierCurve.prototype = Object.create( Curve.prototype ); - CubicBezierCurve.prototype.constructor = CubicBezierCurve; - - CubicBezierCurve.prototype.isCubicBezierCurve = true; - - CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector2(); - - var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) - ); - - return point; - - }; - - CubicBezierCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); - - return this; - - }; - - CubicBezierCurve.prototype.toJSON = function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); - - return data; - - }; - - CubicBezierCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); - - return this; - - }; - - function CubicBezierCurve3( v0, v1, v2, v3 ) { - - Curve.call( this ); - - this.type = 'CubicBezierCurve3'; - - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - this.v3 = v3 || new Vector3(); - - } - - CubicBezierCurve3.prototype = Object.create( Curve.prototype ); - CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; - - CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; - - CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector3(); - - var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), - CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) - ); - - return point; - - }; - - CubicBezierCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); - - return this; - - }; - - CubicBezierCurve3.prototype.toJSON = function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); - - return data; - - }; - - CubicBezierCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); - - return this; - - }; - - function LineCurve( v1, v2 ) { - - Curve.call( this ); - - this.type = 'LineCurve'; - - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - - } - - LineCurve.prototype = Object.create( Curve.prototype ); - LineCurve.prototype.constructor = LineCurve; - - LineCurve.prototype.isLineCurve = true; - - LineCurve.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector2(); - - if ( t === 1 ) { - - point.copy( this.v2 ); - - } else { - - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); - - } - - return point; - - }; - - // Line curve is linear, so we can overwrite default getPointAt - - LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - - }; - - LineCurve.prototype.getTangent = function ( t, optionalTarget ) { - - var tangent = optionalTarget || new Vector2(); - - var tangent = tangent.copy( this.v2 ).sub( this.v1 ).normalize(); - - return tangent; - - }; - - LineCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - LineCurve.prototype.toJSON = function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - LineCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function LineCurve3( v1, v2 ) { - - Curve.call( this ); - - this.type = 'LineCurve3'; - - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - - } - - LineCurve3.prototype = Object.create( Curve.prototype ); - LineCurve3.prototype.constructor = LineCurve3; - - LineCurve3.prototype.isLineCurve3 = true; - - LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector3(); - - if ( t === 1 ) { - - point.copy( this.v2 ); - - } else { - - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); - - } - - return point; - - }; - - // Line curve is linear, so we can overwrite default getPointAt - - LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - - }; - - LineCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - LineCurve3.prototype.toJSON = function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - LineCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function QuadraticBezierCurve( v0, v1, v2 ) { - - Curve.call( this ); - - this.type = 'QuadraticBezierCurve'; - - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - - } - - QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; - - QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; - - QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector2(); - - var v0 = this.v0, v1 = this.v1, v2 = this.v2; - - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ) - ); - - return point; - - }; - - QuadraticBezierCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - QuadraticBezierCurve.prototype.toJSON = function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - QuadraticBezierCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function QuadraticBezierCurve3( v0, v1, v2 ) { - - Curve.call( this ); - - this.type = 'QuadraticBezierCurve3'; - - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - - } - - QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; - - QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; - - QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector3(); - - var v0 = this.v0, v1 = this.v1, v2 = this.v2; - - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ), - QuadraticBezier( t, v0.z, v1.z, v2.z ) - ); - - return point; - - }; - - QuadraticBezierCurve3.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - - return this; - - }; - - QuadraticBezierCurve3.prototype.toJSON = function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; - - }; - - QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - - return this; - - }; - - function SplineCurve( points /* array of Vector2 */ ) { - - Curve.call( this ); - - this.type = 'SplineCurve'; - - this.points = points || []; - - } - - SplineCurve.prototype = Object.create( Curve.prototype ); - SplineCurve.prototype.constructor = SplineCurve; - - SplineCurve.prototype.isSplineCurve = true; - - SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector2(); - - var points = this.points; - var p = ( points.length - 1 ) * t; - - var intPoint = Math.floor( p ); - var weight = p - intPoint; - - var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - var p1 = points[ intPoint ]; - var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - - point.set( - CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), - CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) - ); - - return point; - - }; - - SplineCurve.prototype.copy = function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.points = []; - - for ( var i = 0, l = source.points.length; i < l; i ++ ) { - - var point = source.points[ i ]; - - this.points.push( point.clone() ); - - } - - return this; - - }; - - SplineCurve.prototype.toJSON = function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.points = []; - - for ( var i = 0, l = this.points.length; i < l; i ++ ) { - - var point = this.points[ i ]; - data.points.push( point.toArray() ); - - } - - return data; - - }; - - SplineCurve.prototype.fromJSON = function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.points = []; - - for ( var i = 0, l = json.points.length; i < l; i ++ ) { - - var point = json.points[ i ]; - this.points.push( new Vector2().fromArray( point ) ); - - } - - return this; - - }; - - var Curves = /*#__PURE__*/Object.freeze({ - __proto__: null, - ArcCurve: ArcCurve, - CatmullRomCurve3: CatmullRomCurve3, - CubicBezierCurve: CubicBezierCurve, - CubicBezierCurve3: CubicBezierCurve3, - EllipseCurve: EllipseCurve, - LineCurve: LineCurve, - LineCurve3: LineCurve3, - QuadraticBezierCurve: QuadraticBezierCurve, - QuadraticBezierCurve3: QuadraticBezierCurve3, - SplineCurve: SplineCurve - }); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - **/ - - /************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ - - function CurvePath() { - - Curve.call( this ); - - this.type = 'CurvePath'; - - this.curves = []; - this.autoClose = false; // Automatically closes the path - - } - - CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { - - constructor: CurvePath, - - add: function ( curve ) { - - this.curves.push( curve ); - - }, - - closePath: function () { - - // Add a line curve if start and end of lines are not connected - var startPoint = this.curves[ 0 ].getPoint( 0 ); - var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - - if ( ! startPoint.equals( endPoint ) ) { - - this.curves.push( new LineCurve( endPoint, startPoint ) ); - - } - - }, - - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: - - // 1. Length of each sub path have to be known - // 2. Locate and identify type of curve - // 3. Get t for the curve - // 4. Return curve.getPointAt(t') - - getPoint: function ( t ) { - - var d = t * this.getLength(); - var curveLengths = this.getCurveLengths(); - var i = 0; - - // To think about boundaries points. - - while ( i < curveLengths.length ) { - - if ( curveLengths[ i ] >= d ) { - - var diff = curveLengths[ i ] - d; - var curve = this.curves[ i ]; - - var segmentLength = curve.getLength(); - var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - - return curve.getPointAt( u ); - - } - - i ++; - - } - - return null; - - // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { - - points.push( points[ 0 ] ); - - } - - return points; - - }, - - copy: function ( source ) { - - Curve.prototype.copy.call( this, source ); - - this.curves = []; - - for ( var i = 0, l = source.curves.length; i < l; i ++ ) { - - var curve = source.curves[ i ]; - - this.curves.push( curve.clone() ); - - } - - this.autoClose = source.autoClose; - - return this; - - }, - - toJSON: function () { - - var data = Curve.prototype.toJSON.call( this ); - - data.autoClose = this.autoClose; - data.curves = []; - - for ( var i = 0, l = this.curves.length; i < l; i ++ ) { - - var curve = this.curves[ i ]; - data.curves.push( curve.toJSON() ); - - } - - return data; - - }, - - fromJSON: function ( json ) { - - Curve.prototype.fromJSON.call( this, json ); - - this.autoClose = json.autoClose; - this.curves = []; - - for ( var i = 0, l = json.curves.length; i < l; i ++ ) { - - var curve = json.curves[ i ]; - this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); - - } - - return this; - - } - - } ); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Creates free form 2d path using series of points, lines or curves. - **/ - - function Path( points ) { - - CurvePath.call( this ); - - this.type = 'Path'; - - this.currentPoint = new Vector2(); - - if ( points ) { - - this.setFromPoints( points ); - - } - - } - - Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { - - constructor: Path, - - setFromPoints: function ( points ) { - - this.moveTo( points[ 0 ].x, points[ 0 ].y ); - - for ( var i = 1, l = points.length; i < l; i ++ ) { - - this.lineTo( points[ i ].x, points[ i ].y ); - - } - - return this; - - }, - - moveTo: function ( x, y ) { - - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - - return this; - - }, - - lineTo: function ( x, y ) { - - var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); - - this.currentPoint.set( x, y ); - - return this; - - }, - - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - - var curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - return this; - - }, - - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - - var curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); - - return this; - - }, - - splineThru: function ( pts /*Array of Vector*/ ) { - - var npts = [ this.currentPoint.clone() ].concat( pts ); - - var curve = new SplineCurve( npts ); - this.curves.push( curve ); - - this.currentPoint.copy( pts[ pts.length - 1 ] ); - - return this; - - }, - - arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - var x0 = this.currentPoint.x; - var y0 = this.currentPoint.y; - - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); - - return this; - - }, - - absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - - return this; - - }, - - ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - var x0 = this.currentPoint.x; - var y0 = this.currentPoint.y; - - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - return this; - - }, - - absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - - var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - - if ( this.curves.length > 0 ) { - - // if a previous curve is present, attempt to join - var firstPoint = curve.getPoint( 0 ); - - if ( ! firstPoint.equals( this.currentPoint ) ) { - - this.lineTo( firstPoint.x, firstPoint.y ); - - } - - } - - this.curves.push( curve ); - - var lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); - - return this; - - }, - - copy: function ( source ) { - - CurvePath.prototype.copy.call( this, source ); - - this.currentPoint.copy( source.currentPoint ); - - return this; - - }, - - toJSON: function () { - - var data = CurvePath.prototype.toJSON.call( this ); - - data.currentPoint = this.currentPoint.toArray(); - - return data; - - }, - - fromJSON: function ( json ) { - - CurvePath.prototype.fromJSON.call( this, json ); - - this.currentPoint.fromArray( json.currentPoint ); - - return this; - - } - - } ); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Defines a 2d shape plane using paths. - **/ - - // STEP 1 Create a path. - // STEP 2 Turn path into shape. - // STEP 3 ExtrudeGeometry takes in Shape/Shapes - // STEP 3a - Extract points from each shape, turn to vertices - // STEP 3b - Triangulate each shape, add faces. - - function Shape( points ) { - - Path.call( this, points ); - - this.uuid = MathUtils.generateUUID(); - - this.type = 'Shape'; - - this.holes = []; - - } - - Shape.prototype = Object.assign( Object.create( Path.prototype ), { - - constructor: Shape, - - getPointsHoles: function ( divisions ) { - - var holesPts = []; - - for ( var i = 0, l = this.holes.length; i < l; i ++ ) { - - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); - - } - - return holesPts; - - }, - - // get points of shape and holes (keypoints based on segments parameter) - - extractPoints: function ( divisions ) { - - return { - - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) - - }; - - }, - - copy: function ( source ) { - - Path.prototype.copy.call( this, source ); - - this.holes = []; - - for ( var i = 0, l = source.holes.length; i < l; i ++ ) { - - var hole = source.holes[ i ]; - - this.holes.push( hole.clone() ); - - } - - return this; - - }, - - toJSON: function () { - - var data = Path.prototype.toJSON.call( this ); - - data.uuid = this.uuid; - data.holes = []; - - for ( var i = 0, l = this.holes.length; i < l; i ++ ) { - - var hole = this.holes[ i ]; - data.holes.push( hole.toJSON() ); - - } - - return data; - - }, - - fromJSON: function ( json ) { - - Path.prototype.fromJSON.call( this, json ); - - this.uuid = json.uuid; - this.holes = []; - - for ( var i = 0, l = json.holes.length; i < l; i ++ ) { - - var hole = json.holes[ i ]; - this.holes.push( new Path().fromJSON( hole ) ); - - } - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function Light( color, intensity ) { - - Object3D.call( this ); - - this.type = 'Light'; - - this.color = new Color( color ); - this.intensity = intensity !== undefined ? intensity : 1; - - this.receiveShadow = undefined; - - } - - Light.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Light, - - isLight: true, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.intensity = source.intensity; - - return this; - - }, - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; - - if ( this.groundColor !== undefined ) { data.object.groundColor = this.groundColor.getHex(); } - - if ( this.distance !== undefined ) { data.object.distance = this.distance; } - if ( this.angle !== undefined ) { data.object.angle = this.angle; } - if ( this.decay !== undefined ) { data.object.decay = this.decay; } - if ( this.penumbra !== undefined ) { data.object.penumbra = this.penumbra; } - - if ( this.shadow !== undefined ) { data.object.shadow = this.shadow.toJSON(); } - - return data; - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function HemisphereLight( skyColor, groundColor, intensity ) { - - Light.call( this, skyColor, intensity ); - - this.type = 'HemisphereLight'; - - this.castShadow = undefined; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.groundColor = new Color( groundColor ); - - } - - HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: HemisphereLight, - - isHemisphereLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.groundColor.copy( source.groundColor ); - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function LightShadow( camera ) { - - this.camera = camera; - - this.bias = 0; - this.radius = 1; - - this.mapSize = new Vector2( 512, 512 ); - - this.map = null; - this.mapPass = null; - this.matrix = new Matrix4(); - - this._frustum = new Frustum(); - this._frameExtents = new Vector2( 1, 1 ); - - this._viewportCount = 1; - - this._viewports = [ - - new Vector4( 0, 0, 1, 1 ) - - ]; - - } - - Object.assign( LightShadow.prototype, { - - _projScreenMatrix: new Matrix4(), - - _lightPositionWorld: new Vector3(), - - _lookTarget: new Vector3(), - - getViewportCount: function () { - - return this._viewportCount; - - }, - - getFrustum: function () { - - return this._frustum; - - }, - - updateMatrices: function ( light ) { - - var shadowCamera = this.camera, - shadowMatrix = this.matrix, - projScreenMatrix = this._projScreenMatrix, - lookTarget = this._lookTarget, - lightPositionWorld = this._lightPositionWorld; - - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( lightPositionWorld ); - - lookTarget.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( lookTarget ); - shadowCamera.updateMatrixWorld(); - - projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( projScreenMatrix ); - - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); - - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - - }, - - getViewport: function ( viewportIndex ) { - - return this._viewports[ viewportIndex ]; - - }, - - getFrameExtents: function () { - - return this._frameExtents; - - }, - - copy: function ( source ) { - - this.camera = source.camera.clone(); - - this.bias = source.bias; - this.radius = source.radius; - - this.mapSize.copy( source.mapSize ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - toJSON: function () { - - var object = {}; - - if ( this.bias !== 0 ) { object.bias = this.bias; } - if ( this.radius !== 1 ) { object.radius = this.radius; } - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) { object.mapSize = this.mapSize.toArray(); } - - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; - - return object; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function SpotLightShadow() { - - LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - - } - - SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: SpotLightShadow, - - isSpotLightShadow: true, - - updateMatrices: function ( light ) { - - var camera = this.camera; - - var fov = MathUtils.RAD2DEG * 2 * light.angle; - var aspect = this.mapSize.width / this.mapSize.height; - var far = light.distance || camera.far; - - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); - - } - - LightShadow.prototype.updateMatrices.call( this, light ); - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function SpotLight( color, intensity, distance, angle, penumbra, decay ) { - - Light.call( this, color, intensity ); - - this.type = 'SpotLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - Object.defineProperty( this, 'power', { - get: function () { - - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * Math.PI; - - }, - set: function ( power ) { - - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / Math.PI; - - } - } ); - - this.distance = ( distance !== undefined ) ? distance : 0; - this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; - this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - - this.shadow = new SpotLightShadow(); - - } - - SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: SpotLight, - - isSpotLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; - - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - function PointLightShadow() { - - LightShadow.call( this, new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - - this._frameExtents = new Vector2( 4, 2 ); - - this._viewportCount = 6; - - this._viewports = [ - // These viewports map a cube-map onto a 2D texture with the - // following orientation: - // - // xzXZ - // y Y - // - // X - Positive x direction - // x - Negative x direction - // Y - Positive y direction - // y - Negative y direction - // Z - Positive z direction - // z - Negative z direction - - // positive X - new Vector4( 2, 1, 1, 1 ), - // negative X - new Vector4( 0, 1, 1, 1 ), - // positive Z - new Vector4( 3, 1, 1, 1 ), - // negative Z - new Vector4( 1, 1, 1, 1 ), - // positive Y - new Vector4( 3, 0, 1, 1 ), - // negative Y - new Vector4( 1, 0, 1, 1 ) - ]; - - this._cubeDirections = [ - new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), - new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) - ]; - - this._cubeUps = [ - new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), - new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) - ]; - - } - - PointLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: PointLightShadow, - - isPointLightShadow: true, - - updateMatrices: function ( light, viewportIndex ) { - - if ( viewportIndex === undefined ) { viewportIndex = 0; } - - var camera = this.camera, - shadowMatrix = this.matrix, - lightPositionWorld = this._lightPositionWorld, - lookTarget = this._lookTarget, - projScreenMatrix = this._projScreenMatrix; - - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - camera.position.copy( lightPositionWorld ); - - lookTarget.copy( camera.position ); - lookTarget.add( this._cubeDirections[ viewportIndex ] ); - camera.up.copy( this._cubeUps[ viewportIndex ] ); - camera.lookAt( lookTarget ); - camera.updateMatrixWorld(); - - shadowMatrix.makeTranslation( - lightPositionWorld.x, - lightPositionWorld.y, - lightPositionWorld.z ); - - projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( projScreenMatrix ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - - function PointLight( color, intensity, distance, decay ) { - - Light.call( this, color, intensity ); - - this.type = 'PointLight'; - - Object.defineProperty( this, 'power', { - get: function () { - - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * 4 * Math.PI; - - }, - set: function ( power ) { - - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / ( 4 * Math.PI ); - - } - } ); - - this.distance = ( distance !== undefined ) ? distance : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - - this.shadow = new PointLightShadow(); - - } - - PointLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: PointLight, - - isPointLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.distance = source.distance; - this.decay = source.decay; - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author arose / http://github.com/arose - */ - - function OrthographicCamera( left, right, top, bottom, near, far ) { - - Camera.call( this ); - - this.type = 'OrthographicCamera'; - - this.zoom = 1; - this.view = null; - - this.left = ( left !== undefined ) ? left : - 1; - this.right = ( right !== undefined ) ? right : 1; - this.top = ( top !== undefined ) ? top : 1; - this.bottom = ( bottom !== undefined ) ? bottom : - 1; - - this.near = ( near !== undefined ) ? near : 0.1; - this.far = ( far !== undefined ) ? far : 2000; - - this.updateProjectionMatrix(); - - } - - OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { - - constructor: OrthographicCamera, - - isOrthographicCamera: true, - - copy: function ( source, recursive ) { - - Camera.prototype.copy.call( this, source, recursive ); - - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; - - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - return this; - - }, - - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { - - if ( this.view === null ) { - - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; - - } - - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; - - this.updateProjectionMatrix(); - - }, - - clearViewOffset: function () { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - }, - - updateProjectionMatrix: function () { - - var dx = ( this.right - this.left ) / ( 2 * this.zoom ); - var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - var cx = ( this.right + this.left ) / 2; - var cy = ( this.top + this.bottom ) / 2; - - var left = cx - dx; - var right = cx + dx; - var top = cy + dy; - var bottom = cy - dy; - - if ( this.view !== null && this.view.enabled ) { - - var scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; - var scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; - - left += scaleW * this.view.offsetX; - right = left + scaleW * this.view.width; - top -= scaleH * this.view.offsetY; - bottom = top - scaleH * this.view.height; - - } - - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - - this.projectionMatrixInverse.getInverse( this.projectionMatrix ); - - }, - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, meta ); - - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; - - if ( this.view !== null ) { data.object.view = Object.assign( {}, this.view ); } - - return data; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function DirectionalLightShadow() { - - LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); - - } - - DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - - constructor: DirectionalLightShadow, - - isDirectionalLightShadow: true, - - updateMatrices: function ( light ) { - - LightShadow.prototype.updateMatrices.call( this, light ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function DirectionalLight( color, intensity ) { - - Light.call( this, color, intensity ); - - this.type = 'DirectionalLight'; - - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); - - this.target = new Object3D(); - - this.shadow = new DirectionalLightShadow(); - - } - - DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: DirectionalLight, - - isDirectionalLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function AmbientLight( color, intensity ) { - - Light.call( this, color, intensity ); - - this.type = 'AmbientLight'; - - this.castShadow = undefined; - - } - - AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: AmbientLight, - - isAmbientLight: true - - } ); - - /** - * @author abelnation / http://github.com/abelnation - */ - - function RectAreaLight( color, intensity, width, height ) { - - Light.call( this, color, intensity ); - - this.type = 'RectAreaLight'; - - this.width = ( width !== undefined ) ? width : 10; - this.height = ( height !== undefined ) ? height : 10; - - } - - RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: RectAreaLight, - - isRectAreaLight: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.width = source.width; - this.height = source.height; - - return this; - - }, - - toJSON: function ( meta ) { - - var data = Light.prototype.toJSON.call( this, meta ); - - data.object.width = this.width; - data.object.height = this.height; - - return data; - - } - - } ); - - /** - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - * - * Primary reference: - * https://graphics.stanford.edu/papers/envmap/envmap.pdf - * - * Secondary reference: - * https://www.ppsloan.org/publications/StupidSH36.pdf - */ - - // 3-band SH defined by 9 coefficients - - function SphericalHarmonics3() { - - this.coefficients = []; - - for ( var i = 0; i < 9; i ++ ) { - - this.coefficients.push( new Vector3() ); - - } - - } - - Object.assign( SphericalHarmonics3.prototype, { - - isSphericalHarmonics3: true, - - set: function ( coefficients ) { - - for ( var i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].copy( coefficients[ i ] ); - - } - - return this; - - }, - - zero: function () { - - for ( var i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].set( 0, 0, 0 ); - - } - - return this; - - }, - - // get the radiance in the direction of the normal - // target is a Vector3 - getAt: function ( normal, target ) { - - // normal is assumed to be unit length - - var x = normal.x, y = normal.y, z = normal.z; - - var coeff = this.coefficients; - - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); - - // band 1 - target.addScaledVector( coeff[ 1 ], 0.488603 * y ); - target.addScaledVector( coeff[ 2 ], 0.488603 * z ); - target.addScaledVector( coeff[ 3 ], 0.488603 * x ); - - // band 2 - target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); - target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); - target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); - target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); - target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); - - return target; - - }, - - // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal - // target is a Vector3 - // https://graphics.stanford.edu/papers/envmap/envmap.pdf - getIrradianceAt: function ( normal, target ) { - - // normal is assumed to be unit length - - var x = normal.x, y = normal.y, z = normal.z; - - var coeff = this.coefficients; - - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 - - // band 1 - target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 - target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); - target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); - - // band 2 - target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 - target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); - target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 - target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); - target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 - - return target; - - }, - - add: function ( sh ) { - - for ( var i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].add( sh.coefficients[ i ] ); - - } - - return this; - - }, - - addScaledSH: function ( sh, s ) { - - for ( var i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); - - } - - return this; - - }, - - scale: function ( s ) { - - for ( var i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].multiplyScalar( s ); - - } - - return this; - - }, - - lerp: function ( sh, alpha ) { - - for ( var i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); - - } - - return this; - - }, - - equals: function ( sh ) { - - for ( var i = 0; i < 9; i ++ ) { - - if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { - - return false; - - } - - } - - return true; - - }, - - copy: function ( sh ) { - - return this.set( sh.coefficients ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - fromArray: function ( array, offset ) { - - if ( offset === undefined ) { offset = 0; } - - var coefficients = this.coefficients; - - for ( var i = 0; i < 9; i ++ ) { - - coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); - - } - - return this; - - }, - - toArray: function ( array, offset ) { - - if ( array === undefined ) { array = []; } - if ( offset === undefined ) { offset = 0; } - - var coefficients = this.coefficients; - - for ( var i = 0; i < 9; i ++ ) { - - coefficients[ i ].toArray( array, offset + ( i * 3 ) ); - - } - - return array; - - } - - } ); - - Object.assign( SphericalHarmonics3, { - - // evaluate the basis functions - // shBasis is an Array[ 9 ] - getBasisAt: function ( normal, shBasis ) { - - // normal is assumed to be unit length - - var x = normal.x, y = normal.y, z = normal.z; - - // band 0 - shBasis[ 0 ] = 0.282095; - - // band 1 - shBasis[ 1 ] = 0.488603 * y; - shBasis[ 2 ] = 0.488603 * z; - shBasis[ 3 ] = 0.488603 * x; - - // band 2 - shBasis[ 4 ] = 1.092548 * x * y; - shBasis[ 5 ] = 1.092548 * y * z; - shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); - shBasis[ 7 ] = 1.092548 * x * z; - shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); - - } - - } ); - - /** - * @author WestLangley / http://github.com/WestLangley - * - * A LightProbe is a source of indirect-diffuse light - */ - - function LightProbe( sh, intensity ) { - - Light.call( this, undefined, intensity ); - - this.type = 'LightProbe'; - - this.sh = ( sh !== undefined ) ? sh : new SphericalHarmonics3(); - - } - - LightProbe.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: LightProbe, - - isLightProbe: true, - - copy: function ( source ) { - - Light.prototype.copy.call( this, source ); - - this.sh.copy( source.sh ); - - return this; - - }, - - fromJSON: function ( json ) { - - this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); - this.sh.fromArray( json.sh ); - - return this; - - }, - - toJSON: function ( meta ) { - - var data = Light.prototype.toJSON.call( this, meta ); - - data.object.sh = this.sh.toArray(); - - return data; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function MaterialLoader( manager ) { - - Loader.call( this, manager ); - - this.textures = {}; - - } - - MaterialLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: MaterialLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - var textures = this.textures; - - function getTexture( name ) { - - if ( textures[ name ] === undefined ) { - - console.warn( 'THREE.MaterialLoader: Undefined texture', name ); - - } - - return textures[ name ]; - - } - - var material = new Materials[ json.type ](); - - if ( json.uuid !== undefined ) { material.uuid = json.uuid; } - if ( json.name !== undefined ) { material.name = json.name; } - if ( json.color !== undefined ) { material.color.setHex( json.color ); } - if ( json.roughness !== undefined ) { material.roughness = json.roughness; } - if ( json.metalness !== undefined ) { material.metalness = json.metalness; } - if ( json.sheen !== undefined ) { material.sheen = new Color().setHex( json.sheen ); } - if ( json.emissive !== undefined ) { material.emissive.setHex( json.emissive ); } - if ( json.specular !== undefined ) { material.specular.setHex( json.specular ); } - if ( json.shininess !== undefined ) { material.shininess = json.shininess; } - if ( json.clearcoat !== undefined ) { material.clearcoat = json.clearcoat; } - if ( json.clearcoatRoughness !== undefined ) { material.clearcoatRoughness = json.clearcoatRoughness; } - if ( json.fog !== undefined ) { material.fog = json.fog; } - if ( json.flatShading !== undefined ) { material.flatShading = json.flatShading; } - if ( json.blending !== undefined ) { material.blending = json.blending; } - if ( json.combine !== undefined ) { material.combine = json.combine; } - if ( json.side !== undefined ) { material.side = json.side; } - if ( json.opacity !== undefined ) { material.opacity = json.opacity; } - if ( json.transparent !== undefined ) { material.transparent = json.transparent; } - if ( json.alphaTest !== undefined ) { material.alphaTest = json.alphaTest; } - if ( json.depthTest !== undefined ) { material.depthTest = json.depthTest; } - if ( json.depthWrite !== undefined ) { material.depthWrite = json.depthWrite; } - if ( json.colorWrite !== undefined ) { material.colorWrite = json.colorWrite; } - - if ( json.stencilWrite !== undefined ) { material.stencilWrite = json.stencilWrite; } - if ( json.stencilWriteMask !== undefined ) { material.stencilWriteMask = json.stencilWriteMask; } - if ( json.stencilFunc !== undefined ) { material.stencilFunc = json.stencilFunc; } - if ( json.stencilRef !== undefined ) { material.stencilRef = json.stencilRef; } - if ( json.stencilFuncMask !== undefined ) { material.stencilFuncMask = json.stencilFuncMask; } - if ( json.stencilFail !== undefined ) { material.stencilFail = json.stencilFail; } - if ( json.stencilZFail !== undefined ) { material.stencilZFail = json.stencilZFail; } - if ( json.stencilZPass !== undefined ) { material.stencilZPass = json.stencilZPass; } - - if ( json.wireframe !== undefined ) { material.wireframe = json.wireframe; } - if ( json.wireframeLinewidth !== undefined ) { material.wireframeLinewidth = json.wireframeLinewidth; } - if ( json.wireframeLinecap !== undefined ) { material.wireframeLinecap = json.wireframeLinecap; } - if ( json.wireframeLinejoin !== undefined ) { material.wireframeLinejoin = json.wireframeLinejoin; } - - if ( json.rotation !== undefined ) { material.rotation = json.rotation; } - - if ( json.linewidth !== 1 ) { material.linewidth = json.linewidth; } - if ( json.dashSize !== undefined ) { material.dashSize = json.dashSize; } - if ( json.gapSize !== undefined ) { material.gapSize = json.gapSize; } - if ( json.scale !== undefined ) { material.scale = json.scale; } - - if ( json.polygonOffset !== undefined ) { material.polygonOffset = json.polygonOffset; } - if ( json.polygonOffsetFactor !== undefined ) { material.polygonOffsetFactor = json.polygonOffsetFactor; } - if ( json.polygonOffsetUnits !== undefined ) { material.polygonOffsetUnits = json.polygonOffsetUnits; } - - if ( json.skinning !== undefined ) { material.skinning = json.skinning; } - if ( json.morphTargets !== undefined ) { material.morphTargets = json.morphTargets; } - if ( json.morphNormals !== undefined ) { material.morphNormals = json.morphNormals; } - if ( json.dithering !== undefined ) { material.dithering = json.dithering; } - - if ( json.vertexTangents !== undefined ) { material.vertexTangents = json.vertexTangents; } - - if ( json.visible !== undefined ) { material.visible = json.visible; } - - if ( json.toneMapped !== undefined ) { material.toneMapped = json.toneMapped; } - - if ( json.userData !== undefined ) { material.userData = json.userData; } - - if ( json.vertexColors !== undefined ) { - - if ( typeof json.vertexColors === 'number' ) { - - material.vertexColors = ( json.vertexColors > 0 ) ? true : false; - - } else { - - material.vertexColors = json.vertexColors; - - } - - } - - // Shader Material - - if ( json.uniforms !== undefined ) { - - for ( var name in json.uniforms ) { - - var uniform = json.uniforms[ name ]; - - material.uniforms[ name ] = {}; - - switch ( uniform.type ) { - - case 't': - material.uniforms[ name ].value = getTexture( uniform.value ); - break; - - case 'c': - material.uniforms[ name ].value = new Color().setHex( uniform.value ); - break; - - case 'v2': - material.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); - break; - - case 'v3': - material.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); - break; - - case 'v4': - material.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); - break; - - case 'm3': - material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); - - case 'm4': - material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); - break; - - default: - material.uniforms[ name ].value = uniform.value; - - } - - } - - } - - if ( json.defines !== undefined ) { material.defines = json.defines; } - if ( json.vertexShader !== undefined ) { material.vertexShader = json.vertexShader; } - if ( json.fragmentShader !== undefined ) { material.fragmentShader = json.fragmentShader; } - - if ( json.extensions !== undefined ) { - - for ( var key in json.extensions ) { - - material.extensions[ key ] = json.extensions[ key ]; - - } - - } - - // Deprecated - - if ( json.shading !== undefined ) { material.flatShading = json.shading === 1; } // THREE.FlatShading - - // for PointsMaterial - - if ( json.size !== undefined ) { material.size = json.size; } - if ( json.sizeAttenuation !== undefined ) { material.sizeAttenuation = json.sizeAttenuation; } - - // maps - - if ( json.map !== undefined ) { material.map = getTexture( json.map ); } - if ( json.matcap !== undefined ) { material.matcap = getTexture( json.matcap ); } - - if ( json.alphaMap !== undefined ) { material.alphaMap = getTexture( json.alphaMap ); } - - if ( json.bumpMap !== undefined ) { material.bumpMap = getTexture( json.bumpMap ); } - if ( json.bumpScale !== undefined ) { material.bumpScale = json.bumpScale; } - - if ( json.normalMap !== undefined ) { material.normalMap = getTexture( json.normalMap ); } - if ( json.normalMapType !== undefined ) { material.normalMapType = json.normalMapType; } - if ( json.normalScale !== undefined ) { - - var normalScale = json.normalScale; - - if ( Array.isArray( normalScale ) === false ) { - - // Blender exporter used to export a scalar. See #7459 - - normalScale = [ normalScale, normalScale ]; - - } - - material.normalScale = new Vector2().fromArray( normalScale ); - - } - - if ( json.displacementMap !== undefined ) { material.displacementMap = getTexture( json.displacementMap ); } - if ( json.displacementScale !== undefined ) { material.displacementScale = json.displacementScale; } - if ( json.displacementBias !== undefined ) { material.displacementBias = json.displacementBias; } - - if ( json.roughnessMap !== undefined ) { material.roughnessMap = getTexture( json.roughnessMap ); } - if ( json.metalnessMap !== undefined ) { material.metalnessMap = getTexture( json.metalnessMap ); } - - if ( json.emissiveMap !== undefined ) { material.emissiveMap = getTexture( json.emissiveMap ); } - if ( json.emissiveIntensity !== undefined ) { material.emissiveIntensity = json.emissiveIntensity; } - - if ( json.specularMap !== undefined ) { material.specularMap = getTexture( json.specularMap ); } - - if ( json.envMap !== undefined ) { material.envMap = getTexture( json.envMap ); } - if ( json.envMapIntensity !== undefined ) { material.envMapIntensity = json.envMapIntensity; } - - if ( json.reflectivity !== undefined ) { material.reflectivity = json.reflectivity; } - if ( json.refractionRatio !== undefined ) { material.refractionRatio = json.refractionRatio; } - - if ( json.lightMap !== undefined ) { material.lightMap = getTexture( json.lightMap ); } - if ( json.lightMapIntensity !== undefined ) { material.lightMapIntensity = json.lightMapIntensity; } - - if ( json.aoMap !== undefined ) { material.aoMap = getTexture( json.aoMap ); } - if ( json.aoMapIntensity !== undefined ) { material.aoMapIntensity = json.aoMapIntensity; } - - if ( json.gradientMap !== undefined ) { material.gradientMap = getTexture( json.gradientMap ); } - - if ( json.clearcoatMap !== undefined ) { material.clearcoatMap = getTexture( json.clearcoatMap ); } - if ( json.clearcoatRoughnessMap !== undefined ) { material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap ); } - if ( json.clearcoatNormalMap !== undefined ) { material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap ); } - if ( json.clearcoatNormalScale !== undefined ) { material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale ); } - - return material; - - }, - - setTextures: function ( value ) { - - this.textures = value; - return this; - - } - - } ); - - /** - * @author Don McCurdy / https://www.donmccurdy.com - */ - - var LoaderUtils = { - - decodeText: function ( array ) { - - if ( typeof TextDecoder !== 'undefined' ) { - - return new TextDecoder().decode( array ); - - } - - // Avoid the String.fromCharCode.apply(null, array) shortcut, which - // throws a "maximum call stack size exceeded" error for large arrays. - - var s = ''; - - for ( var i = 0, il = array.length; i < il; i ++ ) { - - // Implicitly assumes little-endian. - s += String.fromCharCode( array[ i ] ); - - } - - try { - - // merges multi-byte utf-8 characters. - - return decodeURIComponent( escape( s ) ); - - } catch ( e ) { // see #16358 - - return s; - - } - - }, - - extractUrlBase: function ( url ) { - - var index = url.lastIndexOf( '/' ); - - if ( index === - 1 ) { return './'; } - - return url.substr( 0, index + 1 ); - - } - - }; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InstancedBufferGeometry() { - - BufferGeometry.call( this ); - - this.type = 'InstancedBufferGeometry'; - this.instanceCount = Infinity; - - } - - InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), { - - constructor: InstancedBufferGeometry, - - isInstancedBufferGeometry: true, - - copy: function ( source ) { - - BufferGeometry.prototype.copy.call( this, source ); - - this.instanceCount = source.instanceCount; - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - toJSON: function () { - - var data = BufferGeometry.prototype.toJSON.call( this ); - - data.instanceCount = this.instanceCount; - - data.isInstancedBufferGeometry = true; - - return data; - - } - - } ); - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InstancedBufferAttribute( array, itemSize, normalized, meshPerAttribute ) { - - if ( typeof ( normalized ) === 'number' ) { - - meshPerAttribute = normalized; - - normalized = false; - - console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); - - } - - BufferAttribute.call( this, array, itemSize, normalized ); - - this.meshPerAttribute = meshPerAttribute || 1; - - } - - InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), { - - constructor: InstancedBufferAttribute, - - isInstancedBufferAttribute: true, - - copy: function ( source ) { - - BufferAttribute.prototype.copy.call( this, source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - }, - - toJSON: function () { - - var data = BufferAttribute.prototype.toJSON.call( this ); - - data.meshPerAttribute = this.meshPerAttribute; - - data.isInstancedBufferAttribute = true; - - return data; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function BufferGeometryLoader( manager ) { - - Loader.call( this, manager ); - - } - - BufferGeometryLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: BufferGeometryLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.load( url, function ( text ) { - - try { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - var geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); - - var index = json.data.index; - - if ( index !== undefined ) { - - var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); - geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); - - } - - var attributes = json.data.attributes; - - for ( var key in attributes ) { - - var attribute = attributes[ key ]; - var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - var bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; - var bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized ); - if ( attribute.name !== undefined ) { bufferAttribute.name = attribute.name; } - geometry.setAttribute( key, bufferAttribute ); - - } - - var morphAttributes = json.data.morphAttributes; - - if ( morphAttributes ) { - - for ( var key in morphAttributes ) { - - var attributeArray = morphAttributes[ key ]; - - var array = []; - - for ( var i = 0, il = attributeArray.length; i < il; i ++ ) { - - var attribute = attributeArray[ i ]; - var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - - var bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); - if ( attribute.name !== undefined ) { bufferAttribute.name = attribute.name; } - array.push( bufferAttribute ); - - } - - geometry.morphAttributes[ key ] = array; - - } - - } - - var morphTargetsRelative = json.data.morphTargetsRelative; - - if ( morphTargetsRelative ) { - - geometry.morphTargetsRelative = true; - - } - - var groups = json.data.groups || json.data.drawcalls || json.data.offsets; - - if ( groups !== undefined ) { - - for ( var i = 0, n = groups.length; i !== n; ++ i ) { - - var group = groups[ i ]; - - geometry.addGroup( group.start, group.count, group.materialIndex ); - - } - - } - - var boundingSphere = json.data.boundingSphere; - - if ( boundingSphere !== undefined ) { - - var center = new Vector3(); - - if ( boundingSphere.center !== undefined ) { - - center.fromArray( boundingSphere.center ); - - } - - geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); - - } - - if ( json.name ) { geometry.name = json.name; } - if ( json.userData ) { geometry.userData = json.userData; } - - return geometry; - - } - - } ); - - var TYPED_ARRAYS = { - Int8Array: Int8Array, - Uint8Array: Uint8Array, - // Workaround for IE11 pre KB2929437. See #11440 - Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array, - Int16Array: Int16Array, - Uint16Array: Uint16Array, - Int32Array: Int32Array, - Uint32Array: Uint32Array, - Float32Array: Float32Array, - Float64Array: Float64Array - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function ObjectLoader( manager ) { - - Loader.call( this, manager ); - - } - - ObjectLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: ObjectLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path; - this.resourcePath = this.resourcePath || path; - - var loader = new FileLoader( scope.manager ); - loader.setPath( this.path ); - loader.load( url, function ( text ) { - - var json = null; - - try { - - json = JSON.parse( text ); - - } catch ( error ) { - - if ( onError !== undefined ) { onError( error ); } - - console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); - - return; - - } - - var metadata = json.metadata; - - if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { - - console.error( 'THREE.ObjectLoader: Can\'t load ' + url ); - return; - - } - - scope.parse( json, onLoad ); - - }, onProgress, onError ); - - }, - - parse: function ( json, onLoad ) { - - var shapes = this.parseShape( json.shapes ); - var geometries = this.parseGeometries( json.geometries, shapes ); - - var images = this.parseImages( json.images, function () { - - if ( onLoad !== undefined ) { onLoad( object ); } - - } ); - - var textures = this.parseTextures( json.textures, images ); - var materials = this.parseMaterials( json.materials, textures ); - - var object = this.parseObject( json.object, geometries, materials ); - - if ( json.animations ) { - - object.animations = this.parseAnimations( json.animations ); - - } - - if ( json.images === undefined || json.images.length === 0 ) { - - if ( onLoad !== undefined ) { onLoad( object ); } - - } - - return object; - - }, - - parseShape: function ( json ) { - - var shapes = {}; - - if ( json !== undefined ) { - - for ( var i = 0, l = json.length; i < l; i ++ ) { - - var shape = new Shape().fromJSON( json[ i ] ); - - shapes[ shape.uuid ] = shape; - - } - - } - - return shapes; - - }, - - parseGeometries: function ( json, shapes ) { - - var geometries = {}; - - if ( json !== undefined ) { - - var bufferGeometryLoader = new BufferGeometryLoader(); - - for ( var i = 0, l = json.length; i < l; i ++ ) { - - var geometry; - var data = json[ i ]; - - switch ( data.type ) { - - case 'PlaneGeometry': - case 'PlaneBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.widthSegments, - data.heightSegments - ); - - break; - - case 'BoxGeometry': - case 'BoxBufferGeometry': - case 'CubeGeometry': // backwards compatible - - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.depth, - data.widthSegments, - data.heightSegments, - data.depthSegments - ); - - break; - - case 'CircleGeometry': - case 'CircleBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.segments, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'CylinderGeometry': - case 'CylinderBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radiusTop, - data.radiusBottom, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'ConeGeometry': - case 'ConeBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'SphereGeometry': - case 'SphereBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.widthSegments, - data.heightSegments, - data.phiStart, - data.phiLength, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'DodecahedronGeometry': - case 'DodecahedronBufferGeometry': - case 'IcosahedronGeometry': - case 'IcosahedronBufferGeometry': - case 'OctahedronGeometry': - case 'OctahedronBufferGeometry': - case 'TetrahedronGeometry': - case 'TetrahedronBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.detail - ); - - break; - - case 'RingGeometry': - case 'RingBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.innerRadius, - data.outerRadius, - data.thetaSegments, - data.phiSegments, - data.thetaStart, - data.thetaLength - ); - - break; - - case 'TorusGeometry': - case 'TorusBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.arc - ); - - break; - - case 'TorusKnotGeometry': - case 'TorusKnotBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.tubularSegments, - data.radialSegments, - data.p, - data.q - ); - - break; - - case 'TubeGeometry': - case 'TubeBufferGeometry': - - // This only works for built-in curves (e.g. CatmullRomCurve3). - // User defined curves or instances of CurvePath will not be deserialized. - geometry = new Geometries[ data.type ]( - new Curves[ data.path.type ]().fromJSON( data.path ), - data.tubularSegments, - data.radius, - data.radialSegments, - data.closed - ); - - break; - - case 'LatheGeometry': - case 'LatheBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.points, - data.segments, - data.phiStart, - data.phiLength - ); - - break; - - case 'PolyhedronGeometry': - case 'PolyhedronBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.vertices, - data.indices, - data.radius, - data.details - ); - - break; - - case 'ShapeGeometry': - case 'ShapeBufferGeometry': - - var geometryShapes = []; - - for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { - - var shape = shapes[ data.shapes[ j ] ]; - - geometryShapes.push( shape ); - - } - - geometry = new Geometries[ data.type ]( - geometryShapes, - data.curveSegments - ); - - break; - - - case 'ExtrudeGeometry': - case 'ExtrudeBufferGeometry': - - var geometryShapes = []; - - for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { - - var shape = shapes[ data.shapes[ j ] ]; - - geometryShapes.push( shape ); - - } - - var extrudePath = data.options.extrudePath; - - if ( extrudePath !== undefined ) { - - data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); - - } - - geometry = new Geometries[ data.type ]( - geometryShapes, - data.options - ); - - break; - - case 'BufferGeometry': - case 'InstancedBufferGeometry': - - geometry = bufferGeometryLoader.parse( data ); - - break; - - case 'Geometry': - - console.error( 'THREE.ObjectLoader: Loading "Geometry" is not supported anymore.' ); - - break; - - default: - - console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); - - continue; - - } - - geometry.uuid = data.uuid; - - if ( data.name !== undefined ) { geometry.name = data.name; } - if ( geometry.isBufferGeometry === true && data.userData !== undefined ) { geometry.userData = data.userData; } - - geometries[ data.uuid ] = geometry; - - } - - } - - return geometries; - - }, - - parseMaterials: function ( json, textures ) { - - var cache = {}; // MultiMaterial - var materials = {}; - - if ( json !== undefined ) { - - var loader = new MaterialLoader(); - loader.setTextures( textures ); - - for ( var i = 0, l = json.length; i < l; i ++ ) { - - var data = json[ i ]; - - if ( data.type === 'MultiMaterial' ) { - - // Deprecated - - var array = []; - - for ( var j = 0; j < data.materials.length; j ++ ) { - - var material = data.materials[ j ]; - - if ( cache[ material.uuid ] === undefined ) { - - cache[ material.uuid ] = loader.parse( material ); - - } - - array.push( cache[ material.uuid ] ); - - } - - materials[ data.uuid ] = array; - - } else { - - if ( cache[ data.uuid ] === undefined ) { - - cache[ data.uuid ] = loader.parse( data ); - - } - - materials[ data.uuid ] = cache[ data.uuid ]; - - } - - } - - } - - return materials; - - }, - - parseAnimations: function ( json ) { - - var animations = []; - - for ( var i = 0; i < json.length; i ++ ) { - - var data = json[ i ]; - - var clip = AnimationClip.parse( data ); - - if ( data.uuid !== undefined ) { clip.uuid = data.uuid; } - - animations.push( clip ); - - } - - return animations; - - }, - - parseImages: function ( json, onLoad ) { - - var scope = this; - var images = {}; - - function loadImage( url ) { - - scope.manager.itemStart( url ); - - return loader.load( url, function () { - - scope.manager.itemEnd( url ); - - }, undefined, function () { - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } ); - - } - - if ( json !== undefined && json.length > 0 ) { - - var manager = new LoadingManager( onLoad ); - - var loader = new ImageLoader( manager ); - loader.setCrossOrigin( this.crossOrigin ); - - for ( var i = 0, il = json.length; i < il; i ++ ) { - - var image = json[ i ]; - var url = image.url; - - if ( Array.isArray( url ) ) { - - // load array of images e.g CubeTexture - - images[ image.uuid ] = []; - - for ( var j = 0, jl = url.length; j < jl; j ++ ) { - - var currentUrl = url[ j ]; - - var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( currentUrl ) ? currentUrl : scope.resourcePath + currentUrl; - - images[ image.uuid ].push( loadImage( path ) ); - - } - - } else { - - // load single image - - var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.resourcePath + image.url; - - images[ image.uuid ] = loadImage( path ); - - } - - } - - } - - return images; - - }, - - parseTextures: function ( json, images ) { - - function parseConstant( value, type ) { - - if ( typeof value === 'number' ) { return value; } - - console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); - - return type[ value ]; - - } - - var textures = {}; - - if ( json !== undefined ) { - - for ( var i = 0, l = json.length; i < l; i ++ ) { - - var data = json[ i ]; - - if ( data.image === undefined ) { - - console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); - - } - - if ( images[ data.image ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); - - } - - var texture; - - if ( Array.isArray( images[ data.image ] ) ) { - - texture = new CubeTexture( images[ data.image ] ); - - } else { - - texture = new Texture( images[ data.image ] ); - - } - - texture.needsUpdate = true; - - texture.uuid = data.uuid; - - if ( data.name !== undefined ) { texture.name = data.name; } - - if ( data.mapping !== undefined ) { texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); } - - if ( data.offset !== undefined ) { texture.offset.fromArray( data.offset ); } - if ( data.repeat !== undefined ) { texture.repeat.fromArray( data.repeat ); } - if ( data.center !== undefined ) { texture.center.fromArray( data.center ); } - if ( data.rotation !== undefined ) { texture.rotation = data.rotation; } - - if ( data.wrap !== undefined ) { - - texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); - texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); - - } - - if ( data.format !== undefined ) { texture.format = data.format; } - if ( data.type !== undefined ) { texture.type = data.type; } - if ( data.encoding !== undefined ) { texture.encoding = data.encoding; } - - if ( data.minFilter !== undefined ) { texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); } - if ( data.magFilter !== undefined ) { texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); } - if ( data.anisotropy !== undefined ) { texture.anisotropy = data.anisotropy; } - - if ( data.flipY !== undefined ) { texture.flipY = data.flipY; } - - if ( data.premultiplyAlpha !== undefined ) { texture.premultiplyAlpha = data.premultiplyAlpha; } - if ( data.unpackAlignment !== undefined ) { texture.unpackAlignment = data.unpackAlignment; } - - textures[ data.uuid ] = texture; - - } - - } - - return textures; - - }, - - parseObject: function ( data, geometries, materials ) { - - var object; - - function getGeometry( name ) { - - if ( geometries[ name ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); - - } - - return geometries[ name ]; - - } - - function getMaterial( name ) { - - if ( name === undefined ) { return undefined; } - - if ( Array.isArray( name ) ) { - - var array = []; - - for ( var i = 0, l = name.length; i < l; i ++ ) { - - var uuid = name[ i ]; - - if ( materials[ uuid ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined material', uuid ); - - } - - array.push( materials[ uuid ] ); - - } - - return array; - - } - - if ( materials[ name ] === undefined ) { - - console.warn( 'THREE.ObjectLoader: Undefined material', name ); - - } - - return materials[ name ]; - - } - - switch ( data.type ) { - - case 'Scene': - - object = new Scene(); - - if ( data.background !== undefined ) { - - if ( Number.isInteger( data.background ) ) { - - object.background = new Color( data.background ); - - } - - } - - if ( data.fog !== undefined ) { - - if ( data.fog.type === 'Fog' ) { - - object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); - - } else if ( data.fog.type === 'FogExp2' ) { - - object.fog = new FogExp2( data.fog.color, data.fog.density ); - - } - - } - - break; - - case 'PerspectiveCamera': - - object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); - - if ( data.focus !== undefined ) { object.focus = data.focus; } - if ( data.zoom !== undefined ) { object.zoom = data.zoom; } - if ( data.filmGauge !== undefined ) { object.filmGauge = data.filmGauge; } - if ( data.filmOffset !== undefined ) { object.filmOffset = data.filmOffset; } - if ( data.view !== undefined ) { object.view = Object.assign( {}, data.view ); } - - break; - - case 'OrthographicCamera': - - object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); - - if ( data.zoom !== undefined ) { object.zoom = data.zoom; } - if ( data.view !== undefined ) { object.view = Object.assign( {}, data.view ); } - - break; - - case 'AmbientLight': - - object = new AmbientLight( data.color, data.intensity ); - - break; - - case 'DirectionalLight': - - object = new DirectionalLight( data.color, data.intensity ); - - break; - - case 'PointLight': - - object = new PointLight( data.color, data.intensity, data.distance, data.decay ); - - break; - - case 'RectAreaLight': - - object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); - - break; - - case 'SpotLight': - - object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); - - break; - - case 'HemisphereLight': - - object = new HemisphereLight( data.color, data.groundColor, data.intensity ); - - break; - - case 'LightProbe': - - object = new LightProbe().fromJSON( data ); - - break; - - case 'SkinnedMesh': - - console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' ); - - case 'Mesh': - - var geometry = getGeometry( data.geometry ); - var material = getMaterial( data.material ); - - object = new Mesh( geometry, material ); - - break; - - case 'InstancedMesh': - - var geometry = getGeometry( data.geometry ); - var material = getMaterial( data.material ); - var count = data.count; - var instanceMatrix = data.instanceMatrix; - - object = new InstancedMesh( geometry, material, count ); - object.instanceMatrix = new BufferAttribute( new Float32Array( instanceMatrix.array ), 16 ); - - break; - - case 'LOD': - - object = new LOD(); - - break; - - case 'Line': - - object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); - - break; - - case 'LineLoop': - - object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'LineSegments': - - object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'PointCloud': - case 'Points': - - object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); - - break; - - case 'Sprite': - - object = new Sprite( getMaterial( data.material ) ); - - break; - - case 'Group': - - object = new Group(); - - break; - - default: - - object = new Object3D(); - - } - - object.uuid = data.uuid; - - if ( data.name !== undefined ) { object.name = data.name; } - - if ( data.matrix !== undefined ) { - - object.matrix.fromArray( data.matrix ); - - if ( data.matrixAutoUpdate !== undefined ) { object.matrixAutoUpdate = data.matrixAutoUpdate; } - if ( object.matrixAutoUpdate ) { object.matrix.decompose( object.position, object.quaternion, object.scale ); } - - } else { - - if ( data.position !== undefined ) { object.position.fromArray( data.position ); } - if ( data.rotation !== undefined ) { object.rotation.fromArray( data.rotation ); } - if ( data.quaternion !== undefined ) { object.quaternion.fromArray( data.quaternion ); } - if ( data.scale !== undefined ) { object.scale.fromArray( data.scale ); } - - } - - if ( data.castShadow !== undefined ) { object.castShadow = data.castShadow; } - if ( data.receiveShadow !== undefined ) { object.receiveShadow = data.receiveShadow; } - - if ( data.shadow ) { - - if ( data.shadow.bias !== undefined ) { object.shadow.bias = data.shadow.bias; } - if ( data.shadow.radius !== undefined ) { object.shadow.radius = data.shadow.radius; } - if ( data.shadow.mapSize !== undefined ) { object.shadow.mapSize.fromArray( data.shadow.mapSize ); } - if ( data.shadow.camera !== undefined ) { object.shadow.camera = this.parseObject( data.shadow.camera ); } - - } - - if ( data.visible !== undefined ) { object.visible = data.visible; } - if ( data.frustumCulled !== undefined ) { object.frustumCulled = data.frustumCulled; } - if ( data.renderOrder !== undefined ) { object.renderOrder = data.renderOrder; } - if ( data.userData !== undefined ) { object.userData = data.userData; } - if ( data.layers !== undefined ) { object.layers.mask = data.layers; } - - if ( data.children !== undefined ) { - - var children = data.children; - - for ( var i = 0; i < children.length; i ++ ) { - - object.add( this.parseObject( children[ i ], geometries, materials ) ); - - } - - } - - if ( data.type === 'LOD' ) { - - if ( data.autoUpdate !== undefined ) { object.autoUpdate = data.autoUpdate; } - - var levels = data.levels; - - for ( var l = 0; l < levels.length; l ++ ) { - - var level = levels[ l ]; - var child = object.getObjectByProperty( 'uuid', level.object ); - - if ( child !== undefined ) { - - object.addLevel( child, level.distance ); - - } - - } - - } - - return object; - - } - - } ); - - var TEXTURE_MAPPING = { - UVMapping: UVMapping, - CubeReflectionMapping: CubeReflectionMapping, - CubeRefractionMapping: CubeRefractionMapping, - EquirectangularReflectionMapping: EquirectangularReflectionMapping, - EquirectangularRefractionMapping: EquirectangularRefractionMapping, - SphericalReflectionMapping: SphericalReflectionMapping, - CubeUVReflectionMapping: CubeUVReflectionMapping, - CubeUVRefractionMapping: CubeUVRefractionMapping - }; - - var TEXTURE_WRAPPING = { - RepeatWrapping: RepeatWrapping, - ClampToEdgeWrapping: ClampToEdgeWrapping, - MirroredRepeatWrapping: MirroredRepeatWrapping - }; - - var TEXTURE_FILTER = { - NearestFilter: NearestFilter, - NearestMipmapNearestFilter: NearestMipmapNearestFilter, - NearestMipmapLinearFilter: NearestMipmapLinearFilter, - LinearFilter: LinearFilter, - LinearMipmapNearestFilter: LinearMipmapNearestFilter, - LinearMipmapLinearFilter: LinearMipmapLinearFilter - }; - - /** - * @author thespite / http://clicktorelease.com/ - */ - - - function ImageBitmapLoader( manager ) { - - if ( typeof createImageBitmap === 'undefined' ) { - - console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); - - } - - if ( typeof fetch === 'undefined' ) { - - console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); - - } - - Loader.call( this, manager ); - - this.options = undefined; - - } - - ImageBitmapLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: ImageBitmapLoader, - - setOptions: function setOptions( options ) { - - this.options = options; - - return this; - - }, - - load: function ( url, onLoad, onProgress, onError ) { - - if ( url === undefined ) { url = ''; } - - if ( this.path !== undefined ) { url = this.path + url; } - - url = this.manager.resolveURL( url ); - - var scope = this; - - var cached = Cache.get( url ); - - if ( cached !== undefined ) { - - scope.manager.itemStart( url ); - - setTimeout( function () { - - if ( onLoad ) { onLoad( cached ); } - - scope.manager.itemEnd( url ); - - }, 0 ); - - return cached; - - } - - fetch( url ).then( function ( res ) { - - return res.blob(); - - } ).then( function ( blob ) { - - if ( scope.options === undefined ) { - - // Workaround for FireFox. It causes an error if you pass options. - return createImageBitmap( blob ); - - } else { - - return createImageBitmap( blob, scope.options ); - - } - - } ).then( function ( imageBitmap ) { - - Cache.add( url, imageBitmap ); - - if ( onLoad ) { onLoad( imageBitmap ); } - - scope.manager.itemEnd( url ); - - } ).catch( function ( e ) { - - if ( onError ) { onError( e ); } - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - } ); - - scope.manager.itemStart( url ); - - } - - } ); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * minimal class for proxing functions to Path. Replaces old "extractSubpaths()" - **/ - - function ShapePath() { - - this.type = 'ShapePath'; - - this.color = new Color(); - - this.subPaths = []; - this.currentPath = null; - - } - - Object.assign( ShapePath.prototype, { - - moveTo: function ( x, y ) { - - this.currentPath = new Path(); - this.subPaths.push( this.currentPath ); - this.currentPath.moveTo( x, y ); - - return this; - - }, - - lineTo: function ( x, y ) { - - this.currentPath.lineTo( x, y ); - - return this; - - }, - - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - - this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); - - return this; - - }, - - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - - this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); - - return this; - - }, - - splineThru: function ( pts ) { - - this.currentPath.splineThru( pts ); - - return this; - - }, - - toShapes: function ( isCCW, noHoles ) { - - function toShapesNoHoles( inSubpaths ) { - - var shapes = []; - - for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { - - var tmpPath = inSubpaths[ i ]; - - var tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - - shapes.push( tmpShape ); - - } - - return shapes; - - } - - function isPointInsidePolygon( inPt, inPolygon ) { - - var polyLen = inPolygon.length; - - // inPt on polygon contour => immediate success or - // toggling of inside/outside at every single! intersection point of an edge - // with the horizontal line through inPt, left of inPt - // not counting lowerY endpoints of edges and whole edges on that line - var inside = false; - for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { - - var edgeLowPt = inPolygon[ p ]; - var edgeHighPt = inPolygon[ q ]; - - var edgeDx = edgeHighPt.x - edgeLowPt.x; - var edgeDy = edgeHighPt.y - edgeLowPt.y; - - if ( Math.abs( edgeDy ) > Number.EPSILON ) { - - // not parallel - if ( edgeDy < 0 ) { - - edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; - edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; - - } - - if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) { continue; } - - if ( inPt.y === edgeLowPt.y ) { - - if ( inPt.x === edgeLowPt.x ) { return true; } // inPt is on contour ? - // continue; // no intersection or edgeLowPt => doesn't count !!! - - } else { - - var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); - if ( perpEdge === 0 ) { return true; } // inPt is on contour ? - if ( perpEdge < 0 ) { continue; } - inside = ! inside; // true intersection left of inPt - - } - - } else { - - // parallel or collinear - if ( inPt.y !== edgeLowPt.y ) { continue; } // parallel - // edge lies on the same horizontal line as inPt - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) { return true; } // inPt: Point on contour ! - // continue; - - } - - } - - return inside; - - } - - var isClockWise = ShapeUtils.isClockWise; - - var subPaths = this.subPaths; - if ( subPaths.length === 0 ) { return []; } - - if ( noHoles === true ) { return toShapesNoHoles( subPaths ); } - - - var solid, tmpPath, tmpShape, shapes = []; - - if ( subPaths.length === 1 ) { - - tmpPath = subPaths[ 0 ]; - tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; - - } - - var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? ! holesFirst : holesFirst; - - // console.log("Holes first", holesFirst); - - var betterShapeHoles = []; - var newShapes = []; - var newShapeHoles = []; - var mainIdx = 0; - var tmpPoints; - - newShapes[ mainIdx ] = undefined; - newShapeHoles[ mainIdx ] = []; - - for ( var i = 0, l = subPaths.length; i < l; i ++ ) { - - tmpPath = subPaths[ i ]; - tmpPoints = tmpPath.getPoints(); - solid = isClockWise( tmpPoints ); - solid = isCCW ? ! solid : solid; - - if ( solid ) { - - if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) { mainIdx ++; } - - newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; - newShapes[ mainIdx ].s.curves = tmpPath.curves; - - if ( holesFirst ) { mainIdx ++; } - newShapeHoles[ mainIdx ] = []; - - //console.log('cw', i); - - } else { - - newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); - - //console.log('ccw', i); - - } - - } - - // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[ 0 ] ) { return toShapesNoHoles( subPaths ); } - - - if ( newShapes.length > 1 ) { - - var ambiguous = false; - var toChange = []; - - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - - betterShapeHoles[ sIdx ] = []; - - } - - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - - var sho = newShapeHoles[ sIdx ]; - - for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { - - var ho = sho[ hIdx ]; - var hole_unassigned = true; - - for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { - - if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { - - if ( sIdx !== s2Idx ) { toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); } - if ( hole_unassigned ) { - - hole_unassigned = false; - betterShapeHoles[ s2Idx ].push( ho ); - - } else { - - ambiguous = true; - - } - - } - - } - - if ( hole_unassigned ) { - - betterShapeHoles[ sIdx ].push( ho ); - - } - - } - - } - // console.log("ambiguous: ", ambiguous); - - if ( toChange.length > 0 ) { - - // console.log("to change: ", toChange); - if ( ! ambiguous ) { newShapeHoles = betterShapeHoles; } - - } - - } - - var tmpHoles; - - for ( var i = 0, il = newShapes.length; i < il; i ++ ) { - - tmpShape = newShapes[ i ].s; - shapes.push( tmpShape ); - tmpHoles = newShapeHoles[ i ]; - - for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { - - tmpShape.holes.push( tmpHoles[ j ].h ); - - } - - } - - //console.log("shape", shapes); - - return shapes; - - } - - } ); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author mrdoob / http://mrdoob.com/ - */ - - - function Font( data ) { - - this.type = 'Font'; - - this.data = data; - - } - - Object.assign( Font.prototype, { - - isFont: true, - - generateShapes: function ( text, size ) { - - if ( size === undefined ) { size = 100; } - - var shapes = []; - var paths = createPaths( text, size, this.data ); - - for ( var p = 0, pl = paths.length; p < pl; p ++ ) { - - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); - - } - - return shapes; - - } - - } ); - - function createPaths( text, size, data ) { - - var chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // workaround for IE11, see #13988 - var scale = size / data.resolution; - var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; - - var paths = []; - - var offsetX = 0, offsetY = 0; - - for ( var i = 0; i < chars.length; i ++ ) { - - var char = chars[ i ]; - - if ( char === '\n' ) { - - offsetX = 0; - offsetY -= line_height; - - } else { - - var ret = createPath( char, scale, offsetX, offsetY, data ); - offsetX += ret.offsetX; - paths.push( ret.path ); - - } - - } - - return paths; - - } - - function createPath( char, scale, offsetX, offsetY, data ) { - - var glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; - - if ( ! glyph ) { - - console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' ); - - return; - - } - - var path = new ShapePath(); - - var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; - - if ( glyph.o ) { - - var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - - for ( var i = 0, l = outline.length; i < l; ) { - - var action = outline[ i ++ ]; - - switch ( action ) { - - case 'm': // moveTo - - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; - - path.moveTo( x, y ); - - break; - - case 'l': // lineTo - - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; - - path.lineTo( x, y ); - - break; - - case 'q': // quadraticCurveTo - - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); - - break; - - case 'b': // bezierCurveTo - - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - cpx2 = outline[ i ++ ] * scale + offsetX; - cpy2 = outline[ i ++ ] * scale + offsetY; - - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); - - break; - - } - - } - - } - - return { offsetX: glyph.ha * scale, path: path }; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function FontLoader( manager ) { - - Loader.call( this, manager ); - - } - - FontLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: FontLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.load( url, function ( text ) { - - var json; - - try { - - json = JSON.parse( text ); - - } catch ( e ) { - - console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); - json = JSON.parse( text.substring( 65, text.length - 2 ) ); - - } - - var font = scope.parse( json ); - - if ( onLoad ) { onLoad( font ); } - - }, onProgress, onError ); - - }, - - parse: function ( json ) { - - return new Font( json ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var _context; - - var AudioContext = { - - getContext: function () { - - if ( _context === undefined ) { - - _context = new ( window.AudioContext || window.webkitAudioContext )(); - - } - - return _context; - - }, - - setContext: function ( value ) { - - _context = value; - - } - - }; - - /** - * @author Reece Aaron Lecrivain / http://reecenotes.com/ - */ - - function AudioLoader( manager ) { - - Loader.call( this, manager ); - - } - - AudioLoader.prototype = Object.assign( Object.create( Loader.prototype ), { - - constructor: AudioLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new FileLoader( scope.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setPath( scope.path ); - loader.load( url, function ( buffer ) { - - try { - - // Create a copy of the buffer. The `decodeAudioData` method - // detaches the buffer when complete, preventing reuse. - var bufferCopy = buffer.slice( 0 ); - - var context = AudioContext.getContext(); - context.decodeAudioData( bufferCopy, function ( audioBuffer ) { - - onLoad( audioBuffer ); - - } ); - - } catch ( e ) { - - if ( onError ) { - - onError( e ); - - } else { - - console.error( e ); - - } - - scope.manager.itemError( url ); - - } - - }, onProgress, onError ); - - } - - } ); - - /** - * @author WestLangley / http://github.com/WestLangley - */ - - function HemisphereLightProbe( skyColor, groundColor, intensity ) { - - LightProbe.call( this, undefined, intensity ); - - var color1 = new Color().set( skyColor ); - var color2 = new Color().set( groundColor ); - - var sky = new Vector3( color1.r, color1.g, color1.b ); - var ground = new Vector3( color2.r, color2.g, color2.b ); - - // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); - var c0 = Math.sqrt( Math.PI ); - var c1 = c0 * Math.sqrt( 0.75 ); - - this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); - this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); - - } - - HemisphereLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - - constructor: HemisphereLightProbe, - - isHemisphereLightProbe: true, - - copy: function ( source ) { // modifying colors not currently supported - - LightProbe.prototype.copy.call( this, source ); - - return this; - - }, - - toJSON: function ( meta ) { - - var data = LightProbe.prototype.toJSON.call( this, meta ); - - // data.sh = this.sh.toArray(); // todo - - return data; - - } - - } ); - - /** - * @author WestLangley / http://github.com/WestLangley - */ - - function AmbientLightProbe( color, intensity ) { - - LightProbe.call( this, undefined, intensity ); - - var color1 = new Color().set( color ); - - // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); - this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); - - } - - AmbientLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - - constructor: AmbientLightProbe, - - isAmbientLightProbe: true, - - copy: function ( source ) { // modifying color not currently supported - - LightProbe.prototype.copy.call( this, source ); - - return this; - - }, - - toJSON: function ( meta ) { - - var data = LightProbe.prototype.toJSON.call( this, meta ); - - // data.sh = this.sh.toArray(); // todo - - return data; - - } - - } ); - - var _eyeRight = new Matrix4(); - var _eyeLeft = new Matrix4(); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function StereoCamera() { - - this.type = 'StereoCamera'; - - this.aspect = 1; - - this.eyeSep = 0.064; - - this.cameraL = new PerspectiveCamera(); - this.cameraL.layers.enable( 1 ); - this.cameraL.matrixAutoUpdate = false; - - this.cameraR = new PerspectiveCamera(); - this.cameraR.layers.enable( 2 ); - this.cameraR.matrixAutoUpdate = false; - - this._cache = { - focus: null, - fov: null, - aspect: null, - near: null, - far: null, - zoom: null, - eyeSep: null - }; - - } - - Object.assign( StereoCamera.prototype, { - - update: function ( camera ) { - - var cache = this._cache; - - var needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || - cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || - cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; - - if ( needsUpdate ) { - - cache.focus = camera.focus; - cache.fov = camera.fov; - cache.aspect = camera.aspect * this.aspect; - cache.near = camera.near; - cache.far = camera.far; - cache.zoom = camera.zoom; - cache.eyeSep = this.eyeSep; - - // Off-axis stereoscopic effect based on - // http://paulbourke.net/stereographics/stereorender/ - - var projectionMatrix = camera.projectionMatrix.clone(); - var eyeSepHalf = cache.eyeSep / 2; - var eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; - var ymax = ( cache.near * Math.tan( MathUtils.DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom; - var xmin, xmax; - - // translate xOffset - - _eyeLeft.elements[ 12 ] = - eyeSepHalf; - _eyeRight.elements[ 12 ] = eyeSepHalf; - - // for left eye - - xmin = - ymax * cache.aspect + eyeSepOnProjection; - xmax = ymax * cache.aspect + eyeSepOnProjection; - - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - - this.cameraL.projectionMatrix.copy( projectionMatrix ); - - // for right eye - - xmin = - ymax * cache.aspect - eyeSepOnProjection; - xmax = ymax * cache.aspect - eyeSepOnProjection; - - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - - this.cameraR.projectionMatrix.copy( projectionMatrix ); - - } - - this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft ); - this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight ); - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function Clock( autoStart ) { - - this.autoStart = ( autoStart !== undefined ) ? autoStart : true; - - this.startTime = 0; - this.oldTime = 0; - this.elapsedTime = 0; - - this.running = false; - - } - - Object.assign( Clock.prototype, { - - start: function () { - - this.startTime = ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 - - this.oldTime = this.startTime; - this.elapsedTime = 0; - this.running = true; - - }, - - stop: function () { - - this.getElapsedTime(); - this.running = false; - this.autoStart = false; - - }, - - getElapsedTime: function () { - - this.getDelta(); - return this.elapsedTime; - - }, - - getDelta: function () { - - var diff = 0; - - if ( this.autoStart && ! this.running ) { - - this.start(); - return 0; - - } - - if ( this.running ) { - - var newTime = ( typeof performance === 'undefined' ? Date : performance ).now(); - - diff = ( newTime - this.oldTime ) / 1000; - this.oldTime = newTime; - - this.elapsedTime += diff; - - } - - return diff; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var _position$2 = new Vector3(); - var _quaternion$3 = new Quaternion(); - var _scale$1 = new Vector3(); - var _orientation = new Vector3(); - - function AudioListener() { - - Object3D.call( this ); - - this.type = 'AudioListener'; - - this.context = AudioContext.getContext(); - - this.gain = this.context.createGain(); - this.gain.connect( this.context.destination ); - - this.filter = null; - - this.timeDelta = 0; - - // private - - this._clock = new Clock(); - - } - - AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: AudioListener, - - getInput: function () { - - return this.gain; - - }, - - removeFilter: function ( ) { - - if ( this.filter !== null ) { - - this.gain.disconnect( this.filter ); - this.filter.disconnect( this.context.destination ); - this.gain.connect( this.context.destination ); - this.filter = null; - - } - - return this; - - }, - - getFilter: function () { - - return this.filter; - - }, - - setFilter: function ( value ) { - - if ( this.filter !== null ) { - - this.gain.disconnect( this.filter ); - this.filter.disconnect( this.context.destination ); - - } else { - - this.gain.disconnect( this.context.destination ); - - } - - this.filter = value; - this.gain.connect( this.filter ); - this.filter.connect( this.context.destination ); - - return this; - - }, - - getMasterVolume: function () { - - return this.gain.gain.value; - - }, - - setMasterVolume: function ( value ) { - - this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); - - return this; - - }, - - updateMatrixWorld: function ( force ) { - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - var listener = this.context.listener; - var up = this.up; - - this.timeDelta = this._clock.getDelta(); - - this.matrixWorld.decompose( _position$2, _quaternion$3, _scale$1 ); - - _orientation.set( 0, 0, - 1 ).applyQuaternion( _quaternion$3 ); - - if ( listener.positionX ) { - - // code path for Chrome (see #14393) - - var endTime = this.context.currentTime + this.timeDelta; - - listener.positionX.linearRampToValueAtTime( _position$2.x, endTime ); - listener.positionY.linearRampToValueAtTime( _position$2.y, endTime ); - listener.positionZ.linearRampToValueAtTime( _position$2.z, endTime ); - listener.forwardX.linearRampToValueAtTime( _orientation.x, endTime ); - listener.forwardY.linearRampToValueAtTime( _orientation.y, endTime ); - listener.forwardZ.linearRampToValueAtTime( _orientation.z, endTime ); - listener.upX.linearRampToValueAtTime( up.x, endTime ); - listener.upY.linearRampToValueAtTime( up.y, endTime ); - listener.upZ.linearRampToValueAtTime( up.z, endTime ); - - } else { - - listener.setPosition( _position$2.x, _position$2.y, _position$2.z ); - listener.setOrientation( _orientation.x, _orientation.y, _orientation.z, up.x, up.y, up.z ); - - } - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author Reece Aaron Lecrivain / http://reecenotes.com/ - */ - - function Audio( listener ) { - - Object3D.call( this ); - - this.type = 'Audio'; - - this.listener = listener; - this.context = listener.context; - - this.gain = this.context.createGain(); - this.gain.connect( listener.getInput() ); - - this.autoplay = false; - - this.buffer = null; - this.detune = 0; - this.loop = false; - this.loopStart = 0; - this.loopEnd = 0; - this.offset = 0; - this.duration = undefined; - this.playbackRate = 1; - this.isPlaying = false; - this.hasPlaybackControl = true; - this.sourceType = 'empty'; - - this._startedAt = 0; - this._progress = 0; - - this.filters = []; - - } - - Audio.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Audio, - - getOutput: function () { - - return this.gain; - - }, - - setNodeSource: function ( audioNode ) { - - this.hasPlaybackControl = false; - this.sourceType = 'audioNode'; - this.source = audioNode; - this.connect(); - - return this; - - }, - - setMediaElementSource: function ( mediaElement ) { - - this.hasPlaybackControl = false; - this.sourceType = 'mediaNode'; - this.source = this.context.createMediaElementSource( mediaElement ); - this.connect(); - - return this; - - }, - - setMediaStreamSource: function ( mediaStream ) { - - this.hasPlaybackControl = false; - this.sourceType = 'mediaStreamNode'; - this.source = this.context.createMediaStreamSource( mediaStream ); - this.connect(); - - return this; - - }, - - setBuffer: function ( audioBuffer ) { - - this.buffer = audioBuffer; - this.sourceType = 'buffer'; - - if ( this.autoplay ) { this.play(); } - - return this; - - }, - - play: function ( delay ) { - - if ( delay === undefined ) { delay = 0; } - - if ( this.isPlaying === true ) { - - console.warn( 'THREE.Audio: Audio is already playing.' ); - return; - - } - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this._startedAt = this.context.currentTime + delay; - - var source = this.context.createBufferSource(); - source.buffer = this.buffer; - source.loop = this.loop; - source.loopStart = this.loopStart; - source.loopEnd = this.loopEnd; - source.onended = this.onEnded.bind( this ); - source.start( this._startedAt, this._progress + this.offset, this.duration ); - - this.isPlaying = true; - - this.source = source; - - this.setDetune( this.detune ); - this.setPlaybackRate( this.playbackRate ); - - return this.connect(); - - }, - - pause: function () { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - if ( this.isPlaying === true ) { - - // update current progress - - this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; - - if ( this.loop === true ) { - - // ensure _progress does not exceed duration with looped audios - - this._progress = this._progress % ( this.duration || this.buffer.duration ); - - } - - this.source.stop(); - this.source.onended = null; - - this.isPlaying = false; - - } - - return this; - - }, - - stop: function () { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this._progress = 0; - - this.source.stop(); - this.source.onended = null; - this.isPlaying = false; - - return this; - - }, - - connect: function () { - - if ( this.filters.length > 0 ) { - - this.source.connect( this.filters[ 0 ] ); - - for ( var i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].connect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); - - } else { - - this.source.connect( this.getOutput() ); - - } - - return this; - - }, - - disconnect: function () { - - if ( this.filters.length > 0 ) { - - this.source.disconnect( this.filters[ 0 ] ); - - for ( var i = 1, l = this.filters.length; i < l; i ++ ) { - - this.filters[ i - 1 ].disconnect( this.filters[ i ] ); - - } - - this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); - - } else { - - this.source.disconnect( this.getOutput() ); - - } - - return this; - - }, - - getFilters: function () { - - return this.filters; - - }, - - setFilters: function ( value ) { - - if ( ! value ) { value = []; } - - if ( this.isPlaying === true ) { - - this.disconnect(); - this.filters = value; - this.connect(); - - } else { - - this.filters = value; - - } - - return this; - - }, - - setDetune: function ( value ) { - - this.detune = value; - - if ( this.source.detune === undefined ) { return; } // only set detune when available - - if ( this.isPlaying === true ) { - - this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); - - } - - return this; - - }, - - getDetune: function () { - - return this.detune; - - }, - - getFilter: function () { - - return this.getFilters()[ 0 ]; - - }, - - setFilter: function ( filter ) { - - return this.setFilters( filter ? [ filter ] : [] ); - - }, - - setPlaybackRate: function ( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.playbackRate = value; - - if ( this.isPlaying === true ) { - - this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); - - } - - return this; - - }, - - getPlaybackRate: function () { - - return this.playbackRate; - - }, - - onEnded: function () { - - this.isPlaying = false; - - }, - - getLoop: function () { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return false; - - } - - return this.loop; - - }, - - setLoop: function ( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; - - } - - this.loop = value; - - if ( this.isPlaying === true ) { - - this.source.loop = this.loop; - - } - - return this; - - }, - - setLoopStart: function ( value ) { - - this.loopStart = value; - - return this; - - }, - - setLoopEnd: function ( value ) { - - this.loopEnd = value; - - return this; - - }, - - getVolume: function () { - - return this.gain.gain.value; - - }, - - setVolume: function ( value ) { - - this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var _position$3 = new Vector3(); - var _quaternion$4 = new Quaternion(); - var _scale$2 = new Vector3(); - var _orientation$1 = new Vector3(); - - function PositionalAudio( listener ) { - - Audio.call( this, listener ); - - this.panner = this.context.createPanner(); - this.panner.panningModel = 'HRTF'; - this.panner.connect( this.gain ); - - } - - PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), { - - constructor: PositionalAudio, - - getOutput: function () { - - return this.panner; - - }, - - getRefDistance: function () { - - return this.panner.refDistance; - - }, - - setRefDistance: function ( value ) { - - this.panner.refDistance = value; - - return this; - - }, - - getRolloffFactor: function () { - - return this.panner.rolloffFactor; - - }, - - setRolloffFactor: function ( value ) { - - this.panner.rolloffFactor = value; - - return this; - - }, - - getDistanceModel: function () { - - return this.panner.distanceModel; - - }, - - setDistanceModel: function ( value ) { - - this.panner.distanceModel = value; - - return this; - - }, - - getMaxDistance: function () { - - return this.panner.maxDistance; - - }, - - setMaxDistance: function ( value ) { - - this.panner.maxDistance = value; - - return this; - - }, - - setDirectionalCone: function ( coneInnerAngle, coneOuterAngle, coneOuterGain ) { - - this.panner.coneInnerAngle = coneInnerAngle; - this.panner.coneOuterAngle = coneOuterAngle; - this.panner.coneOuterGain = coneOuterGain; - - return this; - - }, - - updateMatrixWorld: function ( force ) { - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - if ( this.hasPlaybackControl === true && this.isPlaying === false ) { return; } - - this.matrixWorld.decompose( _position$3, _quaternion$4, _scale$2 ); - - _orientation$1.set( 0, 0, 1 ).applyQuaternion( _quaternion$4 ); - - var panner = this.panner; - - if ( panner.positionX ) { - - // code path for Chrome and Firefox (see #14393) - - var endTime = this.context.currentTime + this.listener.timeDelta; - - panner.positionX.linearRampToValueAtTime( _position$3.x, endTime ); - panner.positionY.linearRampToValueAtTime( _position$3.y, endTime ); - panner.positionZ.linearRampToValueAtTime( _position$3.z, endTime ); - panner.orientationX.linearRampToValueAtTime( _orientation$1.x, endTime ); - panner.orientationY.linearRampToValueAtTime( _orientation$1.y, endTime ); - panner.orientationZ.linearRampToValueAtTime( _orientation$1.z, endTime ); - - } else { - - panner.setPosition( _position$3.x, _position$3.y, _position$3.z ); - panner.setOrientation( _orientation$1.x, _orientation$1.y, _orientation$1.z ); - - } - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function AudioAnalyser( audio, fftSize ) { - - this.analyser = audio.context.createAnalyser(); - this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; - - this.data = new Uint8Array( this.analyser.frequencyBinCount ); - - audio.getOutput().connect( this.analyser ); - - } - - Object.assign( AudioAnalyser.prototype, { - - getFrequencyData: function () { - - this.analyser.getByteFrequencyData( this.data ); - - return this.data; - - }, - - getAverageFrequency: function () { - - var value = 0, data = this.getFrequencyData(); - - for ( var i = 0; i < data.length; i ++ ) { - - value += data[ i ]; - - } - - return value / data.length; - - } - - } ); - - /** - * - * Buffered scene graph property that allows weighted accumulation. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function PropertyMixer( binding, typeName, valueSize ) { - - this.binding = binding; - this.valueSize = valueSize; - - var mixFunction, - mixFunctionAdditive, - setIdentity; - - // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] - // - // interpolators can use .buffer as their .result - // the data then goes to 'incoming' - // - // 'accu0' and 'accu1' are used frame-interleaved for - // the cumulative result and are compared to detect - // changes - // - // 'orig' stores the original state of the property - // - // 'add' is used for additive cumulative results - // - // 'work' is optional and is only present for quaternion types. It is used - // to store intermediate quaternion multiplication results - - switch ( typeName ) { - - case 'quaternion': - mixFunction = this._slerp; - mixFunctionAdditive = this._slerpAdditive; - setIdentity = this._setAdditiveIdentityQuaternion; - - this.buffer = new Float64Array( valueSize * 6 ); - this._workIndex = 5; - break; - - case 'string': - case 'bool': - mixFunction = this._select; - - // Use the regular mix function and for additive on these types, - // additive is not relevant for non-numeric types - mixFunctionAdditive = this._select; - - setIdentity = this._setAdditiveIdentityOther; - - this.buffer = new Array( valueSize * 5 ); - break; - - default: - mixFunction = this._lerp; - mixFunctionAdditive = this._lerpAdditive; - setIdentity = this._setAdditiveIdentityNumeric; - - this.buffer = new Float64Array( valueSize * 5 ); - - } - - this._mixBufferRegion = mixFunction; - this._mixBufferRegionAdditive = mixFunctionAdditive; - this._setIdentity = setIdentity; - this._origIndex = 3; - this._addIndex = 4; - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - this.useCount = 0; - this.referenceCount = 0; - - } - - Object.assign( PropertyMixer.prototype, { - - // accumulate data in the 'incoming' region into 'accu' - accumulate: function ( accuIndex, weight ) { - - // note: happily accumulating nothing when weight = 0, the caller knows - // the weight and shouldn't have made the call in the first place - - var buffer = this.buffer, - stride = this.valueSize, - offset = accuIndex * stride + stride, - - currentWeight = this.cumulativeWeight; - - if ( currentWeight === 0 ) { - - // accuN := incoming * weight - - for ( var i = 0; i !== stride; ++ i ) { - - buffer[ offset + i ] = buffer[ i ]; - - } - - currentWeight = weight; - - } else { - - // accuN := accuN + incoming * weight - - currentWeight += weight; - var mix = weight / currentWeight; - this._mixBufferRegion( buffer, offset, 0, mix, stride ); - - } - - this.cumulativeWeight = currentWeight; - - }, - - // accumulate data in the 'incoming' region into 'add' - accumulateAdditive: function ( weight ) { - - var buffer = this.buffer, - stride = this.valueSize, - offset = stride * this._addIndex; - - if ( this.cumulativeWeightAdditive === 0 ) { - - // add = identity - - this._setIdentity(); - - } - - // add := add + incoming * weight - - this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); - this.cumulativeWeightAdditive += weight; - - }, - - // apply the state of 'accu' to the binding when accus differ - apply: function ( accuIndex ) { - - var stride = this.valueSize, - buffer = this.buffer, - offset = accuIndex * stride + stride, - - weight = this.cumulativeWeight, - weightAdditive = this.cumulativeWeightAdditive, - - binding = this.binding; - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - if ( weight < 1 ) { - - // accuN := accuN + original * ( 1 - cumulativeWeight ) - - var originalValueOffset = stride * this._origIndex; - - this._mixBufferRegion( - buffer, offset, originalValueOffset, 1 - weight, stride ); - - } - - if ( weightAdditive > 0 ) { - - // accuN := accuN + additive accuN - - this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); - - } - - for ( var i = stride, e = stride + stride; i !== e; ++ i ) { - - if ( buffer[ i ] !== buffer[ i + stride ] ) { - - // value has changed -> update scene graph - - binding.setValue( buffer, offset ); - break; - - } - - } - - }, - - // remember the state of the bound property and copy it to both accus - saveOriginalState: function () { - - var binding = this.binding; - - var buffer = this.buffer, - stride = this.valueSize, - - originalValueOffset = stride * this._origIndex; - - binding.getValue( buffer, originalValueOffset ); - - // accu[0..1] := orig -- initially detect changes against the original - for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { - - buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; - - } - - // Add to identity for additive - this._setIdentity(); - - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; - - }, - - // apply the state previously taken via 'saveOriginalState' to the binding - restoreOriginalState: function () { - - var originalValueOffset = this.valueSize * 3; - this.binding.setValue( this.buffer, originalValueOffset ); - - }, - - _setAdditiveIdentityNumeric: function () { - - var startIndex = this._addIndex * this.valueSize; - var endIndex = startIndex + this.valueSize; - - for ( var i = startIndex; i < endIndex; i ++ ) { - - this.buffer[ i ] = 0; - - } - - }, - - _setAdditiveIdentityQuaternion: function () { - - this._setAdditiveIdentityNumeric(); - this.buffer[ this._addIndex * 4 + 3 ] = 1; - - }, - - _setAdditiveIdentityOther: function () { - - var startIndex = this._origIndex * this.valueSize; - var targetIndex = this._addIndex * this.valueSize; - - for ( var i = 0; i < this.valueSize; i ++ ) { - - this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; - - } - - }, - - - // mix functions - - _select: function ( buffer, dstOffset, srcOffset, t, stride ) { - - if ( t >= 0.5 ) { - - for ( var i = 0; i !== stride; ++ i ) { - - buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; - - } - - } - - }, - - _slerp: function ( buffer, dstOffset, srcOffset, t ) { - - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); - - }, - - _slerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) { - - var workOffset = this._workIndex * stride; - - // Store result in intermediate buffer offset - Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); - - // Slerp to the intermediate result - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); - - }, - - _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) { - - var s = 1 - t; - - for ( var i = 0; i !== stride; ++ i ) { - - var j = dstOffset + i; - - buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; - - } - - }, - - _lerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) { - - for ( var i = 0; i !== stride; ++ i ) { - - var j = dstOffset + i; - - buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; - - } - - } - - } ); - - /** - * - * A reference to a real property in the scene graph. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - // Characters [].:/ are reserved for track binding syntax. - var _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; - var _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); - - // Attempts to allow node names from any language. ES5's `\w` regexp matches - // only latin characters, and the unicode \p{L} is not yet supported. So - // instead, we exclude reserved characters and match everything else. - var _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; - var _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; - - // Parent directories, delimited by '/' or ':'. Currently unused, but must - // be matched to parse the rest of the track name. - var _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); - - // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. - var _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); - - // Object on target node, and accessor. May not contain reserved - // characters. Accessor may contain any character except closing bracket. - var _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); - - // Property and accessor. May not contain reserved characters. Accessor may - // contain any non-bracket characters. - var _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); - - var _trackRe = new RegExp( '' - + '^' - + _directoryRe - + _nodeRe - + _objectRe - + _propertyRe - + '$' - ); - - var _supportedObjectNames = [ 'material', 'materials', 'bones' ]; - - function Composite( targetGroup, path, optionalParsedPath ) { - - var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); - - this._targetGroup = targetGroup; - this._bindings = targetGroup.subscribe_( path, parsedPath ); - - } - - Object.assign( Composite.prototype, { - - getValue: function ( array, offset ) { - - this.bind(); // bind all binding - - var firstValidIndex = this._targetGroup.nCachedObjects_, - binding = this._bindings[ firstValidIndex ]; - - // and only call .getValue on the first - if ( binding !== undefined ) { binding.getValue( array, offset ); } - - }, - - setValue: function ( array, offset ) { - - var bindings = this._bindings; - - for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].setValue( array, offset ); - - } - - }, - - bind: function () { - - var bindings = this._bindings; - - for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].bind(); - - } - - }, - - unbind: function () { - - var bindings = this._bindings; - - for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - - bindings[ i ].unbind(); - - } - - } - - } ); - - - function PropertyBinding( rootNode, path, parsedPath ) { - - this.path = path; - this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); - - this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; - - this.rootNode = rootNode; - - } - - Object.assign( PropertyBinding, { - - Composite: Composite, - - create: function ( root, path, parsedPath ) { - - if ( ! ( root && root.isAnimationObjectGroup ) ) { - - return new PropertyBinding( root, path, parsedPath ); - - } else { - - return new PropertyBinding.Composite( root, path, parsedPath ); - - } - - }, - - /** - * Replaces spaces with underscores and removes unsupported characters from - * node names, to ensure compatibility with parseTrackName(). - * - * @param {string} name Node name to be sanitized. - * @return {string} - */ - sanitizeNodeName: function ( name ) { - - return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); - - }, - - parseTrackName: function ( trackName ) { - - var matches = _trackRe.exec( trackName ); - - if ( ! matches ) { - - throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); - - } - - var results = { - // directoryName: matches[ 1 ], // (tschw) currently unused - nodeName: matches[ 2 ], - objectName: matches[ 3 ], - objectIndex: matches[ 4 ], - propertyName: matches[ 5 ], // required - propertyIndex: matches[ 6 ] - }; - - var lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); - - if ( lastDot !== undefined && lastDot !== - 1 ) { - - var objectName = results.nodeName.substring( lastDot + 1 ); - - // Object names must be checked against a whitelist. Otherwise, there - // is no way to parse 'foo.bar.baz': 'baz' must be a property, but - // 'bar' could be the objectName, or part of a nodeName (which can - // include '.' characters). - if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { - - results.nodeName = results.nodeName.substring( 0, lastDot ); - results.objectName = objectName; - - } - - } - - if ( results.propertyName === null || results.propertyName.length === 0 ) { - - throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); - - } - - return results; - - }, - - findNode: function ( root, nodeName ) { - - if ( ! nodeName || nodeName === "" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { - - return root; - - } - - // search into skeleton bones. - if ( root.skeleton ) { - - var bone = root.skeleton.getBoneByName( nodeName ); - - if ( bone !== undefined ) { - - return bone; - - } - - } - - // search into node subtree. - if ( root.children ) { - - var searchNodeSubtree = function ( children ) { - - for ( var i = 0; i < children.length; i ++ ) { - - var childNode = children[ i ]; - - if ( childNode.name === nodeName || childNode.uuid === nodeName ) { - - return childNode; - - } - - var result = searchNodeSubtree( childNode.children ); - - if ( result ) { return result; } - - } - - return null; - - }; - - var subTreeNode = searchNodeSubtree( root.children ); - - if ( subTreeNode ) { - - return subTreeNode; - - } - - } - - return null; - - } - - } ); - - Object.assign( PropertyBinding.prototype, { // prototype, continued - - // these are used to "bind" a nonexistent property - _getValue_unavailable: function () {}, - _setValue_unavailable: function () {}, - - BindingType: { - Direct: 0, - EntireArray: 1, - ArrayElement: 2, - HasFromToArray: 3 - }, - - Versioning: { - None: 0, - NeedsUpdate: 1, - MatrixWorldNeedsUpdate: 2 - }, - - GetterByBindingType: [ - - function getValue_direct( buffer, offset ) { - - buffer[ offset ] = this.node[ this.propertyName ]; - - }, - - function getValue_array( buffer, offset ) { - - var source = this.resolvedProperty; - - for ( var i = 0, n = source.length; i !== n; ++ i ) { - - buffer[ offset ++ ] = source[ i ]; - - } - - }, - - function getValue_arrayElement( buffer, offset ) { - - buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; - - }, - - function getValue_toArray( buffer, offset ) { - - this.resolvedProperty.toArray( buffer, offset ); - - } - - ], - - SetterByBindingTypeAndVersioning: [ - - [ - // Direct - - function setValue_direct( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - - }, - - function setValue_direct_setNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - }, - - function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // EntireArray - - function setValue_array( buffer, offset ) { - - var dest = this.resolvedProperty; - - for ( var i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - }, - - function setValue_array_setNeedsUpdate( buffer, offset ) { - - var dest = this.resolvedProperty; - - for ( var i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.needsUpdate = true; - - }, - - function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { - - var dest = this.resolvedProperty; - - for ( var i = 0, n = dest.length; i !== n; ++ i ) { - - dest[ i ] = buffer[ offset ++ ]; - - } - - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // ArrayElement - - function setValue_arrayElement( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - - }, - - function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; - - }, - - function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ], [ - - // HasToFromArray - - function setValue_fromArray( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - - }, - - function setValue_fromArray_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.needsUpdate = true; - - }, - - function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.matrixWorldNeedsUpdate = true; - - } - - ] - - ], - - getValue: function getValue_unbound( targetArray, offset ) { - - this.bind(); - this.getValue( targetArray, offset ); - - // Note: This class uses a State pattern on a per-method basis: - // 'bind' sets 'this.getValue' / 'setValue' and shadows the - // prototype version of these methods with one that represents - // the bound state. When the property is not found, the methods - // become no-ops. - - }, - - setValue: function getValue_unbound( sourceArray, offset ) { - - this.bind(); - this.setValue( sourceArray, offset ); - - }, - - // create getter / setter pair for a property in the scene graph - bind: function () { - - var targetObject = this.node, - parsedPath = this.parsedPath, - - objectName = parsedPath.objectName, - propertyName = parsedPath.propertyName, - propertyIndex = parsedPath.propertyIndex; - - if ( ! targetObject ) { - - targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; - - this.node = targetObject; - - } - - // set fail state so we can just 'return' on error - this.getValue = this._getValue_unavailable; - this.setValue = this._setValue_unavailable; - - // ensure there is a value node - if ( ! targetObject ) { - - console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); - return; - - } - - if ( objectName ) { - - var objectIndex = parsedPath.objectIndex; - - // special cases were we need to reach deeper into the hierarchy to get the face materials.... - switch ( objectName ) { - - case 'materials': - - if ( ! targetObject.material ) { - - console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); - return; - - } - - if ( ! targetObject.material.materials ) { - - console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); - return; - - } - - targetObject = targetObject.material.materials; - - break; - - case 'bones': - - if ( ! targetObject.skeleton ) { - - console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); - return; - - } - - // potential future optimization: skip this if propertyIndex is already an integer - // and convert the integer string to a true integer. - - targetObject = targetObject.skeleton.bones; - - // support resolving morphTarget names into indices. - for ( var i = 0; i < targetObject.length; i ++ ) { - - if ( targetObject[ i ].name === objectIndex ) { - - objectIndex = i; - break; - - } - - } - - break; - - default: - - if ( targetObject[ objectName ] === undefined ) { - - console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); - return; - - } - - targetObject = targetObject[ objectName ]; - - } - - - if ( objectIndex !== undefined ) { - - if ( targetObject[ objectIndex ] === undefined ) { - - console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); - return; - - } - - targetObject = targetObject[ objectIndex ]; - - } - - } - - // resolve property - var nodeProperty = targetObject[ propertyName ]; - - if ( nodeProperty === undefined ) { - - var nodeName = parsedPath.nodeName; - - console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + - '.' + propertyName + ' but it wasn\'t found.', targetObject ); - return; - - } - - // determine versioning scheme - var versioning = this.Versioning.None; - - this.targetObject = targetObject; - - if ( targetObject.needsUpdate !== undefined ) { // material - - versioning = this.Versioning.NeedsUpdate; - - } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform - - versioning = this.Versioning.MatrixWorldNeedsUpdate; - - } - - // determine how the property gets bound - var bindingType = this.BindingType.Direct; - - if ( propertyIndex !== undefined ) { - - // access a sub element of the property array (only primitives are supported right now) - - if ( propertyName === "morphTargetInfluences" ) { - - // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. - - // support resolving morphTarget names into indices. - if ( ! targetObject.geometry ) { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); - return; - - } - - if ( targetObject.geometry.isBufferGeometry ) { - - if ( ! targetObject.geometry.morphAttributes ) { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); - return; - - } - - if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { - - propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; - - } - - - } else { - - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this ); - return; - - } - - } - - bindingType = this.BindingType.ArrayElement; - - this.resolvedProperty = nodeProperty; - this.propertyIndex = propertyIndex; - - } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { - - // must use copy for Object3D.Euler/Quaternion - - bindingType = this.BindingType.HasFromToArray; - - this.resolvedProperty = nodeProperty; - - } else if ( Array.isArray( nodeProperty ) ) { - - bindingType = this.BindingType.EntireArray; - - this.resolvedProperty = nodeProperty; - - } else { - - this.propertyName = propertyName; - - } - - // select getter / setter - this.getValue = this.GetterByBindingType[ bindingType ]; - this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; - - }, - - unbind: function () { - - this.node = null; - - // back to the prototype version of getValue / setValue - // note: avoiding to mutate the shape of 'this' via 'delete' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; - - } - - } ); - - // DECLARE ALIAS AFTER assign prototype - Object.assign( PropertyBinding.prototype, { - - // initial state of these methods that calls 'bind' - _getValue_unbound: PropertyBinding.prototype.getValue, - _setValue_unbound: PropertyBinding.prototype.setValue, - - } ); - - /** - * - * A group of objects that receives a shared animation state. - * - * Usage: - * - * - Add objects you would otherwise pass as 'root' to the - * constructor or the .clipAction method of AnimationMixer. - * - * - Instead pass this object as 'root'. - * - * - You can also add and remove objects later when the mixer - * is running. - * - * Note: - * - * Objects of this class appear as one object to the mixer, - * so cache control of the individual objects must be done - * on the group. - * - * Limitation: - * - * - The animated properties must be compatible among the - * all objects in the group. - * - * - A single property can either be controlled through a - * target group or directly, but not both. - * - * @author tschw - */ - - function AnimationObjectGroup() { - - this.uuid = MathUtils.generateUUID(); - - // cached objects followed by the active ones - this._objects = Array.prototype.slice.call( arguments ); - - this.nCachedObjects_ = 0; // threshold - // note: read by PropertyBinding.Composite - - var indices = {}; - this._indicesByUUID = indices; // for bookkeeping - - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { - - indices[ arguments[ i ].uuid ] = i; - - } - - this._paths = []; // inside: string - this._parsedPaths = []; // inside: { we don't care, here } - this._bindings = []; // inside: Array< PropertyBinding > - this._bindingsIndicesByPath = {}; // inside: indices in these arrays - - var scope = this; - - this.stats = { - - objects: { - get total() { - - return scope._objects.length; - - }, - get inUse() { - - return this.total - scope.nCachedObjects_; - - } - }, - get bindingsPerObject() { - - return scope._bindings.length; - - } - - }; - - } - - Object.assign( AnimationObjectGroup.prototype, { - - isAnimationObjectGroup: true, - - add: function () { - - var objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - indicesByUUID = this._indicesByUUID, - paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - nBindings = bindings.length, - knownObject = undefined; - - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { - - var object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index === undefined ) { - - // unknown object -> add it to the ACTIVE region - - index = nObjects ++; - indicesByUUID[ uuid ] = index; - objects.push( object ); - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); - - } - - } else if ( index < nCachedObjects ) { - - knownObject = objects[ index ]; - - // move existing object to the ACTIVE region - - var firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ]; - - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - indicesByUUID[ uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - var bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ], - binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = lastCached; - - if ( binding === undefined ) { - - // since we do not bother to create new bindings - // for objects that are cached, the binding may - // or may not exist - - binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); - - } - - bindingsForPath[ firstActiveIndex ] = binding; - - } - - } else if ( objects[ index ] !== knownObject ) { - - console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' + - 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); - - } // else the object is already where we want it to be - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - remove: function () { - - var objects = this._objects, - nCachedObjects = this.nCachedObjects_, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { - - var object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined && index >= nCachedObjects ) { - - // move existing object into the CACHED region - - var lastCachedIndex = nCachedObjects ++, - firstActiveObject = objects[ lastCachedIndex ]; - - indicesByUUID[ firstActiveObject.uuid ] = index; - objects[ index ] = firstActiveObject; - - indicesByUUID[ uuid ] = lastCachedIndex; - objects[ lastCachedIndex ] = object; - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - var bindingsForPath = bindings[ j ], - firstActive = bindingsForPath[ lastCachedIndex ], - binding = bindingsForPath[ index ]; - - bindingsForPath[ index ] = firstActive; - bindingsForPath[ lastCachedIndex ] = binding; - - } - - } - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // remove & forget - uncache: function () { - - var objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; - - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { - - var object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; - - if ( index !== undefined ) { - - delete indicesByUUID[ uuid ]; - - if ( index < nCachedObjects ) { - - // object is cached, shrink the CACHED region - - var firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ], - lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - // last cached object takes this object's place - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; - - // last object goes to the activated slot and pop - indicesByUUID[ lastObject.uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - var bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ], - last = bindingsForPath[ lastIndex ]; - - bindingsForPath[ index ] = lastCached; - bindingsForPath[ firstActiveIndex ] = last; - bindingsForPath.pop(); - - } - - } else { - - // object is active, just swap with the last and pop - - var lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; - - indicesByUUID[ lastObject.uuid ] = index; - objects[ index ] = lastObject; - objects.pop(); - - // accounting is done, now do the same for all bindings - - for ( var j = 0, m = nBindings; j !== m; ++ j ) { - - var bindingsForPath = bindings[ j ]; - - bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; - bindingsForPath.pop(); - - } - - } // cached or active - - } // if object is known - - } // for arguments - - this.nCachedObjects_ = nCachedObjects; - - }, - - // Internal interface used by befriended PropertyBinding.Composite: - - subscribe_: function ( path, parsedPath ) { - - // returns an array of bindings for the given path that is changed - // according to the contained objects in the group - - var indicesByPath = this._bindingsIndicesByPath, - index = indicesByPath[ path ], - bindings = this._bindings; - - if ( index !== undefined ) { return bindings[ index ]; } - - var paths = this._paths, - parsedPaths = this._parsedPaths, - objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - bindingsForPath = new Array( nObjects ); - - index = bindings.length; - - indicesByPath[ path ] = index; - - paths.push( path ); - parsedPaths.push( parsedPath ); - bindings.push( bindingsForPath ); - - for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) { - - var object = objects[ i ]; - bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); - - } - - return bindingsForPath; - - }, - - unsubscribe_: function ( path ) { - - // tells the group to forget about a property path and no longer - // update the array previously obtained with 'subscribe_' - - var indicesByPath = this._bindingsIndicesByPath, - index = indicesByPath[ path ]; - - if ( index !== undefined ) { - - var paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - lastBindingsIndex = bindings.length - 1, - lastBindings = bindings[ lastBindingsIndex ], - lastBindingsPath = path[ lastBindingsIndex ]; - - indicesByPath[ lastBindingsPath ] = index; - - bindings[ index ] = lastBindings; - bindings.pop(); - - parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; - parsedPaths.pop(); - - paths[ index ] = paths[ lastBindingsIndex ]; - paths.pop(); - - } - - } - - } ); - - /** - * - * Action provided by AnimationMixer for scheduling clip playback on specific - * objects. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - * - */ - - function AnimationAction( mixer, clip, localRoot, blendMode ) { - - this._mixer = mixer; - this._clip = clip; - this._localRoot = localRoot || null; - this.blendMode = blendMode || clip.blendMode; - - var tracks = clip.tracks, - nTracks = tracks.length, - interpolants = new Array( nTracks ); - - var interpolantSettings = { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - }; - - for ( var i = 0; i !== nTracks; ++ i ) { - - var interpolant = tracks[ i ].createInterpolant( null ); - interpolants[ i ] = interpolant; - interpolant.settings = interpolantSettings; - - } - - this._interpolantSettings = interpolantSettings; - - this._interpolants = interpolants; // bound by the mixer - - // inside: PropertyMixer (managed by the mixer) - this._propertyBindings = new Array( nTracks ); - - this._cacheIndex = null; // for the memory manager - this._byClipCacheIndex = null; // for the memory manager - - this._timeScaleInterpolant = null; - this._weightInterpolant = null; - - this.loop = LoopRepeat; - this._loopCount = - 1; - - // global mixer time when the action is to be started - // it's set back to 'null' upon start of the action - this._startTime = null; - - // scaled local time of the action - // gets clamped or wrapped to 0..clip.duration according to loop - this.time = 0; - - this.timeScale = 1; - this._effectiveTimeScale = 1; - - this.weight = 1; - this._effectiveWeight = 1; - - this.repetitions = Infinity; // no. of repetitions when looping - - this.paused = false; // true -> zero effective time scale - this.enabled = true; // false -> zero effective weight - - this.clampWhenFinished = false;// keep feeding the last frame? - - this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate - this.zeroSlopeAtEnd = true;// clips for start, loop and end - - } - - Object.assign( AnimationAction.prototype, { - - // State & Scheduling - - play: function () { - - this._mixer._activateAction( this ); - - return this; - - }, - - stop: function () { - - this._mixer._deactivateAction( this ); - - return this.reset(); - - }, - - reset: function () { - - this.paused = false; - this.enabled = true; - - this.time = 0; // restart clip - this._loopCount = - 1;// forget previous loops - this._startTime = null;// forget scheduling - - return this.stopFading().stopWarping(); - - }, - - isRunning: function () { - - return this.enabled && ! this.paused && this.timeScale !== 0 && - this._startTime === null && this._mixer._isActiveAction( this ); - - }, - - // return true when play has been called - isScheduled: function () { - - return this._mixer._isActiveAction( this ); - - }, - - startAt: function ( time ) { - - this._startTime = time; - - return this; - - }, - - setLoop: function ( mode, repetitions ) { - - this.loop = mode; - this.repetitions = repetitions; - - return this; - - }, - - // Weight - - // set the weight stopping any scheduled fading - // although .enabled = false yields an effective weight of zero, this - // method does *not* change .enabled, because it would be confusing - setEffectiveWeight: function ( weight ) { - - this.weight = weight; - - // note: same logic as when updated at runtime - this._effectiveWeight = this.enabled ? weight : 0; - - return this.stopFading(); - - }, - - // return the weight considering fading and .enabled - getEffectiveWeight: function () { - - return this._effectiveWeight; - - }, - - fadeIn: function ( duration ) { - - return this._scheduleFading( duration, 0, 1 ); - - }, - - fadeOut: function ( duration ) { - - return this._scheduleFading( duration, 1, 0 ); - - }, - - crossFadeFrom: function ( fadeOutAction, duration, warp ) { - - fadeOutAction.fadeOut( duration ); - this.fadeIn( duration ); - - if ( warp ) { - - var fadeInDuration = this._clip.duration, - fadeOutDuration = fadeOutAction._clip.duration, - - startEndRatio = fadeOutDuration / fadeInDuration, - endStartRatio = fadeInDuration / fadeOutDuration; - - fadeOutAction.warp( 1.0, startEndRatio, duration ); - this.warp( endStartRatio, 1.0, duration ); - - } - - return this; - - }, - - crossFadeTo: function ( fadeInAction, duration, warp ) { - - return fadeInAction.crossFadeFrom( this, duration, warp ); - - }, - - stopFading: function () { - - var weightInterpolant = this._weightInterpolant; - - if ( weightInterpolant !== null ) { - - this._weightInterpolant = null; - this._mixer._takeBackControlInterpolant( weightInterpolant ); - - } - - return this; - - }, - - // Time Scale Control - - // set the time scale stopping any scheduled warping - // although .paused = true yields an effective time scale of zero, this - // method does *not* change .paused, because it would be confusing - setEffectiveTimeScale: function ( timeScale ) { - - this.timeScale = timeScale; - this._effectiveTimeScale = this.paused ? 0 : timeScale; - - return this.stopWarping(); - - }, - - // return the time scale considering warping and .paused - getEffectiveTimeScale: function () { - - return this._effectiveTimeScale; - - }, - - setDuration: function ( duration ) { - - this.timeScale = this._clip.duration / duration; - - return this.stopWarping(); - - }, - - syncWith: function ( action ) { - - this.time = action.time; - this.timeScale = action.timeScale; - - return this.stopWarping(); - - }, - - halt: function ( duration ) { - - return this.warp( this._effectiveTimeScale, 0, duration ); - - }, - - warp: function ( startTimeScale, endTimeScale, duration ) { - - var mixer = this._mixer, now = mixer.time, - interpolant = this._timeScaleInterpolant, - - timeScale = this.timeScale; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._timeScaleInterpolant = interpolant; - - } - - var times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; - times[ 1 ] = now + duration; - - values[ 0 ] = startTimeScale / timeScale; - values[ 1 ] = endTimeScale / timeScale; - - return this; - - }, - - stopWarping: function () { - - var timeScaleInterpolant = this._timeScaleInterpolant; - - if ( timeScaleInterpolant !== null ) { - - this._timeScaleInterpolant = null; - this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); - - } - - return this; - - }, - - // Object Accessors - - getMixer: function () { - - return this._mixer; - - }, - - getClip: function () { - - return this._clip; - - }, - - getRoot: function () { - - return this._localRoot || this._mixer._root; - - }, - - // Interna - - _update: function ( time, deltaTime, timeDirection, accuIndex ) { - - // called by the mixer - - if ( ! this.enabled ) { - - // call ._updateWeight() to update ._effectiveWeight - - this._updateWeight( time ); - return; - - } - - var startTime = this._startTime; - - if ( startTime !== null ) { - - // check for scheduled start of action - - var timeRunning = ( time - startTime ) * timeDirection; - if ( timeRunning < 0 || timeDirection === 0 ) { - - return; // yet to come / don't decide when delta = 0 - - } - - // start - - this._startTime = null; // unschedule - deltaTime = timeDirection * timeRunning; - - } - - // apply time scale and advance time - - deltaTime *= this._updateTimeScale( time ); - var clipTime = this._updateTime( deltaTime ); - - // note: _updateTime may disable the action resulting in - // an effective weight of 0 - - var weight = this._updateWeight( time ); - - if ( weight > 0 ) { - - var interpolants = this._interpolants; - var propertyMixers = this._propertyBindings; - - switch ( this.blendMode ) { - - case AdditiveAnimationBlendMode: - - for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { - - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulateAdditive( weight ); - - } - - break; - - case NormalAnimationBlendMode: - default: - - for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { - - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulate( accuIndex, weight ); - - } - - } - - } - - }, - - _updateWeight: function ( time ) { - - var weight = 0; - - if ( this.enabled ) { - - weight = this.weight; - var interpolant = this._weightInterpolant; - - if ( interpolant !== null ) { - - var interpolantValue = interpolant.evaluate( time )[ 0 ]; - - weight *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopFading(); - - if ( interpolantValue === 0 ) { - - // faded out, disable - this.enabled = false; - - } - - } - - } - - } - - this._effectiveWeight = weight; - return weight; - - }, - - _updateTimeScale: function ( time ) { - - var timeScale = 0; - - if ( ! this.paused ) { - - timeScale = this.timeScale; - - var interpolant = this._timeScaleInterpolant; - - if ( interpolant !== null ) { - - var interpolantValue = interpolant.evaluate( time )[ 0 ]; - - timeScale *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { - - this.stopWarping(); - - if ( timeScale === 0 ) { - - // motion has halted, pause - this.paused = true; - - } else { - - // warp done - apply final time scale - this.timeScale = timeScale; - - } - - } - - } - - } - - this._effectiveTimeScale = timeScale; - return timeScale; - - }, - - _updateTime: function ( deltaTime ) { - - var time = this.time + deltaTime; - var duration = this._clip.duration; - var loop = this.loop; - var loopCount = this._loopCount; - - var pingPong = ( loop === LoopPingPong ); - - if ( deltaTime === 0 ) { - - if ( loopCount === - 1 ) { return time; } - - return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; - - } - - if ( loop === LoopOnce ) { - - if ( loopCount === - 1 ) { - - // just started - - this._loopCount = 0; - this._setEndings( true, true, false ); - - } - - handle_stop: { - - if ( time >= duration ) { - - time = duration; - - } else if ( time < 0 ) { - - time = 0; - - } else { - - this.time = time; - - break handle_stop; - - } - - if ( this.clampWhenFinished ) { this.paused = true; } - else { this.enabled = false; } - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime < 0 ? - 1 : 1 - } ); - - } - - } else { // repetitive Repeat or PingPong - - if ( loopCount === - 1 ) { - - // just started - - if ( deltaTime >= 0 ) { - - loopCount = 0; - - this._setEndings( true, this.repetitions === 0, pingPong ); - - } else { - - // when looping in reverse direction, the initial - // transition through zero counts as a repetition, - // so leave loopCount at -1 - - this._setEndings( this.repetitions === 0, true, pingPong ); - - } - - } - - if ( time >= duration || time < 0 ) { - - // wrap around - - var loopDelta = Math.floor( time / duration ); // signed - time -= duration * loopDelta; - - loopCount += Math.abs( loopDelta ); - - var pending = this.repetitions - loopCount; - - if ( pending <= 0 ) { - - // have to stop (switch state, clamp time, fire event) - - if ( this.clampWhenFinished ) { this.paused = true; } - else { this.enabled = false; } - - time = deltaTime > 0 ? duration : 0; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime > 0 ? 1 : - 1 - } ); - - } else { - - // keep running - - if ( pending === 1 ) { - - // entering the last round - - var atStart = deltaTime < 0; - this._setEndings( atStart, ! atStart, pingPong ); - - } else { - - this._setEndings( false, false, pingPong ); - - } - - this._loopCount = loopCount; - - this.time = time; - - this._mixer.dispatchEvent( { - type: 'loop', action: this, loopDelta: loopDelta - } ); - - } - - } else { - - this.time = time; - - } - - if ( pingPong && ( loopCount & 1 ) === 1 ) { - - // invert time for the "pong round" - - return duration - time; - - } - - } - - return time; - - }, - - _setEndings: function ( atStart, atEnd, pingPong ) { - - var settings = this._interpolantSettings; - - if ( pingPong ) { - - settings.endingStart = ZeroSlopeEnding; - settings.endingEnd = ZeroSlopeEnding; - - } else { - - // assuming for LoopOnce atStart == atEnd == true - - if ( atStart ) { - - settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingStart = WrapAroundEnding; - - } - - if ( atEnd ) { - - settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; - - } else { - - settings.endingEnd = WrapAroundEnding; - - } - - } - - }, - - _scheduleFading: function ( duration, weightNow, weightThen ) { - - var mixer = this._mixer, now = mixer.time, - interpolant = this._weightInterpolant; - - if ( interpolant === null ) { - - interpolant = mixer._lendControlInterpolant(); - this._weightInterpolant = interpolant; - - } - - var times = interpolant.parameterPositions, - values = interpolant.sampleValues; - - times[ 0 ] = now; - values[ 0 ] = weightNow; - times[ 1 ] = now + duration; - values[ 1 ] = weightThen; - - return this; - - } - - } ); - - /** - * - * Player for AnimationClips. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ - - function AnimationMixer( root ) { - - this._root = root; - this._initMemoryManager(); - this._accuIndex = 0; - - this.time = 0; - - this.timeScale = 1.0; - - } - - AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - - constructor: AnimationMixer, - - _bindAction: function ( action, prototypeAction ) { - - var root = action._localRoot || this._root, - tracks = action._clip.tracks, - nTracks = tracks.length, - bindings = action._propertyBindings, - interpolants = action._interpolants, - rootUuid = root.uuid, - bindingsByRoot = this._bindingsByRootAndName, - bindingsByName = bindingsByRoot[ rootUuid ]; - - if ( bindingsByName === undefined ) { - - bindingsByName = {}; - bindingsByRoot[ rootUuid ] = bindingsByName; - - } - - for ( var i = 0; i !== nTracks; ++ i ) { - - var track = tracks[ i ], - trackName = track.name, - binding = bindingsByName[ trackName ]; - - if ( binding !== undefined ) { - - bindings[ i ] = binding; - - } else { - - binding = bindings[ i ]; - - if ( binding !== undefined ) { - - // existing binding, make sure the cache knows - - if ( binding._cacheIndex === null ) { - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - } - - continue; - - } - - var path = prototypeAction && prototypeAction. - _propertyBindings[ i ].binding.parsedPath; - - binding = new PropertyMixer( - PropertyBinding.create( root, trackName, path ), - track.ValueTypeName, track.getValueSize() ); - - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); - - bindings[ i ] = binding; - - } - - interpolants[ i ].resultBuffer = binding.buffer; - - } - - }, - - _activateAction: function ( action ) { - - if ( ! this._isActiveAction( action ) ) { - - if ( action._cacheIndex === null ) { - - // this action has been forgotten by the cache, but the user - // appears to be still using it -> rebind - - var rootUuid = ( action._localRoot || this._root ).uuid, - clipUuid = action._clip.uuid, - actionsForClip = this._actionsByClip[ clipUuid ]; - - this._bindAction( action, - actionsForClip && actionsForClip.knownActions[ 0 ] ); - - this._addInactiveAction( action, clipUuid, rootUuid ); - - } - - var bindings = action._propertyBindings; - - // increment reference counts / sort out state - for ( var i = 0, n = bindings.length; i !== n; ++ i ) { - - var binding = bindings[ i ]; - - if ( binding.useCount ++ === 0 ) { - - this._lendBinding( binding ); - binding.saveOriginalState(); - - } - - } - - this._lendAction( action ); - - } - - }, - - _deactivateAction: function ( action ) { - - if ( this._isActiveAction( action ) ) { - - var bindings = action._propertyBindings; - - // decrement reference counts / sort out state - for ( var i = 0, n = bindings.length; i !== n; ++ i ) { - - var binding = bindings[ i ]; - - if ( -- binding.useCount === 0 ) { - - binding.restoreOriginalState(); - this._takeBackBinding( binding ); - - } - - } - - this._takeBackAction( action ); - - } - - }, - - // Memory manager - - _initMemoryManager: function () { - - this._actions = []; // 'nActiveActions' followed by inactive ones - this._nActiveActions = 0; - - this._actionsByClip = {}; - // inside: - // { - // knownActions: Array< AnimationAction > - used as prototypes - // actionByRoot: AnimationAction - lookup - // } - - - this._bindings = []; // 'nActiveBindings' followed by inactive ones - this._nActiveBindings = 0; - - this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > - - - this._controlInterpolants = []; // same game as above - this._nActiveControlInterpolants = 0; - - var scope = this; - - this.stats = { - - actions: { - get total() { - - return scope._actions.length; - - }, - get inUse() { - - return scope._nActiveActions; - - } - }, - bindings: { - get total() { - - return scope._bindings.length; - - }, - get inUse() { - - return scope._nActiveBindings; - - } - }, - controlInterpolants: { - get total() { - - return scope._controlInterpolants.length; - - }, - get inUse() { - - return scope._nActiveControlInterpolants; - - } - } - - }; - - }, - - // Memory management for AnimationAction objects - - _isActiveAction: function ( action ) { - - var index = action._cacheIndex; - return index !== null && index < this._nActiveActions; - - }, - - _addInactiveAction: function ( action, clipUuid, rootUuid ) { - - var actions = this._actions, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip === undefined ) { - - actionsForClip = { - - knownActions: [ action ], - actionByRoot: {} - - }; - - action._byClipCacheIndex = 0; - - actionsByClip[ clipUuid ] = actionsForClip; - - } else { - - var knownActions = actionsForClip.knownActions; - - action._byClipCacheIndex = knownActions.length; - knownActions.push( action ); - - } - - action._cacheIndex = actions.length; - actions.push( action ); - - actionsForClip.actionByRoot[ rootUuid ] = action; - - }, - - _removeInactiveAction: function ( action ) { - - var actions = this._actions, - lastInactiveAction = actions[ actions.length - 1 ], - cacheIndex = action._cacheIndex; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - action._cacheIndex = null; - - - var clipUuid = action._clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ], - knownActionsForClip = actionsForClip.knownActions, - - lastKnownAction = - knownActionsForClip[ knownActionsForClip.length - 1 ], - - byClipCacheIndex = action._byClipCacheIndex; - - lastKnownAction._byClipCacheIndex = byClipCacheIndex; - knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; - knownActionsForClip.pop(); - - action._byClipCacheIndex = null; - - - var actionByRoot = actionsForClip.actionByRoot, - rootUuid = ( action._localRoot || this._root ).uuid; - - delete actionByRoot[ rootUuid ]; - - if ( knownActionsForClip.length === 0 ) { - - delete actionsByClip[ clipUuid ]; - - } - - this._removeInactiveBindingsForAction( action ); - - }, - - _removeInactiveBindingsForAction: function ( action ) { - - var bindings = action._propertyBindings; - for ( var i = 0, n = bindings.length; i !== n; ++ i ) { - - var binding = bindings[ i ]; - - if ( -- binding.referenceCount === 0 ) { - - this._removeInactiveBinding( binding ); - - } - - } - - }, - - _lendAction: function ( action ) { - - // [ active actions | inactive actions ] - // [ active actions >| inactive actions ] - // s a - // <-swap-> - // a s - - var actions = this._actions, - prevIndex = action._cacheIndex, - - lastActiveIndex = this._nActiveActions ++, - - firstInactiveAction = actions[ lastActiveIndex ]; - - action._cacheIndex = lastActiveIndex; - actions[ lastActiveIndex ] = action; - - firstInactiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = firstInactiveAction; - - }, - - _takeBackAction: function ( action ) { - - // [ active actions | inactive actions ] - // [ active actions |< inactive actions ] - // a s - // <-swap-> - // s a - - var actions = this._actions, - prevIndex = action._cacheIndex, - - firstInactiveIndex = -- this._nActiveActions, - - lastActiveAction = actions[ firstInactiveIndex ]; - - action._cacheIndex = firstInactiveIndex; - actions[ firstInactiveIndex ] = action; - - lastActiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = lastActiveAction; - - }, - - // Memory management for PropertyMixer objects - - _addInactiveBinding: function ( binding, rootUuid, trackName ) { - - var bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], - - bindings = this._bindings; - - if ( bindingByName === undefined ) { - - bindingByName = {}; - bindingsByRoot[ rootUuid ] = bindingByName; - - } - - bindingByName[ trackName ] = binding; - - binding._cacheIndex = bindings.length; - bindings.push( binding ); - - }, - - _removeInactiveBinding: function ( binding ) { - - var bindings = this._bindings, - propBinding = binding.binding, - rootUuid = propBinding.rootNode.uuid, - trackName = propBinding.path, - bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], - - lastInactiveBinding = bindings[ bindings.length - 1 ], - cacheIndex = binding._cacheIndex; - - lastInactiveBinding._cacheIndex = cacheIndex; - bindings[ cacheIndex ] = lastInactiveBinding; - bindings.pop(); - - delete bindingByName[ trackName ]; - - if ( Object.keys( bindingByName ).length === 0 ) { - - delete bindingsByRoot[ rootUuid ]; - - } - - }, - - _lendBinding: function ( binding ) { - - var bindings = this._bindings, - prevIndex = binding._cacheIndex, - - lastActiveIndex = this._nActiveBindings ++, - - firstInactiveBinding = bindings[ lastActiveIndex ]; - - binding._cacheIndex = lastActiveIndex; - bindings[ lastActiveIndex ] = binding; - - firstInactiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = firstInactiveBinding; - - }, - - _takeBackBinding: function ( binding ) { - - var bindings = this._bindings, - prevIndex = binding._cacheIndex, - - firstInactiveIndex = -- this._nActiveBindings, - - lastActiveBinding = bindings[ firstInactiveIndex ]; - - binding._cacheIndex = firstInactiveIndex; - bindings[ firstInactiveIndex ] = binding; - - lastActiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = lastActiveBinding; - - }, - - - // Memory management of Interpolants for weight and time scale - - _lendControlInterpolant: function () { - - var interpolants = this._controlInterpolants, - lastActiveIndex = this._nActiveControlInterpolants ++, - interpolant = interpolants[ lastActiveIndex ]; - - if ( interpolant === undefined ) { - - interpolant = new LinearInterpolant( - new Float32Array( 2 ), new Float32Array( 2 ), - 1, this._controlInterpolantsResultBuffer ); - - interpolant.__cacheIndex = lastActiveIndex; - interpolants[ lastActiveIndex ] = interpolant; - - } - - return interpolant; - - }, - - _takeBackControlInterpolant: function ( interpolant ) { - - var interpolants = this._controlInterpolants, - prevIndex = interpolant.__cacheIndex, - - firstInactiveIndex = -- this._nActiveControlInterpolants, - - lastActiveInterpolant = interpolants[ firstInactiveIndex ]; - - interpolant.__cacheIndex = firstInactiveIndex; - interpolants[ firstInactiveIndex ] = interpolant; - - lastActiveInterpolant.__cacheIndex = prevIndex; - interpolants[ prevIndex ] = lastActiveInterpolant; - - }, - - _controlInterpolantsResultBuffer: new Float32Array( 1 ), - - // return an action for a clip optionally using a custom root target - // object (this method allocates a lot of dynamic memory in case a - // previously unknown clip/root combination is specified) - clipAction: function ( clip, optionalRoot, blendMode ) { - - var root = optionalRoot || this._root, - rootUuid = root.uuid, - - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, - - clipUuid = clipObject !== null ? clipObject.uuid : clip, - - actionsForClip = this._actionsByClip[ clipUuid ], - prototypeAction = null; - - if ( blendMode === undefined ) { - - if ( clipObject !== null ) { - - blendMode = clipObject.blendMode; - - } else { - - blendMode = NormalAnimationBlendMode; - - } - - } - - if ( actionsForClip !== undefined ) { - - var existingAction = - actionsForClip.actionByRoot[ rootUuid ]; - - if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { - - return existingAction; - - } - - // we know the clip, so we don't have to parse all - // the bindings again but can just copy - prototypeAction = actionsForClip.knownActions[ 0 ]; - - // also, take the clip from the prototype action - if ( clipObject === null ) - { clipObject = prototypeAction._clip; } - - } - - // clip must be known when specified via string - if ( clipObject === null ) { return null; } - - // allocate all resources required to run it - var newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); - - this._bindAction( newAction, prototypeAction ); - - // and make the action known to the memory manager - this._addInactiveAction( newAction, clipUuid, rootUuid ); - - return newAction; - - }, - - // get an existing action - existingAction: function ( clip, optionalRoot ) { - - var root = optionalRoot || this._root, - rootUuid = root.uuid, - - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, - - clipUuid = clipObject ? clipObject.uuid : clip, - - actionsForClip = this._actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - return actionsForClip.actionByRoot[ rootUuid ] || null; - - } - - return null; - - }, - - // deactivates all previously scheduled actions - stopAllAction: function () { - - var actions = this._actions, - nActions = this._nActiveActions; - - for ( var i = nActions - 1; i >= 0; -- i ) { - - actions[ i ].stop(); - - } - - return this; - - }, - - // advance the time and update apply the animation - update: function ( deltaTime ) { - - deltaTime *= this.timeScale; - - var actions = this._actions, - nActions = this._nActiveActions, - - time = this.time += deltaTime, - timeDirection = Math.sign( deltaTime ), - - accuIndex = this._accuIndex ^= 1; - - // run active actions - - for ( var i = 0; i !== nActions; ++ i ) { - - var action = actions[ i ]; - - action._update( time, deltaTime, timeDirection, accuIndex ); - - } - - // update scene graph - - var bindings = this._bindings, - nBindings = this._nActiveBindings; - - for ( var i = 0; i !== nBindings; ++ i ) { - - bindings[ i ].apply( accuIndex ); - - } - - return this; - - }, - - // Allows you to seek to a specific time in an animation. - setTime: function ( timeInSeconds ) { - - this.time = 0; // Zero out time attribute for AnimationMixer object; - for ( var i = 0; i < this._actions.length; i ++ ) { - - this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. - - } - - return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. - - }, - - // return this mixer's root target object - getRoot: function () { - - return this._root; - - }, - - // free all resources specific to a particular clip - uncacheClip: function ( clip ) { - - var actions = this._actions, - clipUuid = clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - // note: just calling _removeInactiveAction would mess up the - // iteration state and also require updating the state we can - // just throw away - - var actionsToRemove = actionsForClip.knownActions; - - for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) { - - var action = actionsToRemove[ i ]; - - this._deactivateAction( action ); - - var cacheIndex = action._cacheIndex, - lastInactiveAction = actions[ actions.length - 1 ]; - - action._cacheIndex = null; - action._byClipCacheIndex = null; - - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); - - this._removeInactiveBindingsForAction( action ); - - } - - delete actionsByClip[ clipUuid ]; - - } - - }, - - // free all resources specific to a particular root target object - uncacheRoot: function ( root ) { - - var rootUuid = root.uuid, - actionsByClip = this._actionsByClip; - - for ( var clipUuid in actionsByClip ) { - - var actionByRoot = actionsByClip[ clipUuid ].actionByRoot, - action = actionByRoot[ rootUuid ]; - - if ( action !== undefined ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - var bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ]; - - if ( bindingByName !== undefined ) { - - for ( var trackName in bindingByName ) { - - var binding = bindingByName[ trackName ]; - binding.restoreOriginalState(); - this._removeInactiveBinding( binding ); - - } - - } - - }, - - // remove a targeted clip from the cache - uncacheAction: function ( clip, optionalRoot ) { - - var action = this.existingAction( clip, optionalRoot ); - - if ( action !== null ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); - - } - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Uniform( value ) { - - if ( typeof value === 'string' ) { - - console.warn( 'THREE.Uniform: Type parameter is no longer needed.' ); - value = arguments[ 1 ]; - - } - - this.value = value; - - } - - Uniform.prototype.clone = function () { - - return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); - - }; - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { - - InterleavedBuffer.call( this, array, stride ); - - this.meshPerAttribute = meshPerAttribute || 1; - - } - - InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), { - - constructor: InstancedInterleavedBuffer, - - isInstancedInterleavedBuffer: true, - - copy: function ( source ) { - - InterleavedBuffer.prototype.copy.call( this, source ); - - this.meshPerAttribute = source.meshPerAttribute; - - return this; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author bhouston / http://clara.io/ - * @author stephomi / http://stephaneginier.com/ - */ - - function Raycaster( origin, direction, near, far ) { - - this.ray = new Ray( origin, direction ); - // direction is assumed to be normalized (for accurate distance calculations) - - this.near = near || 0; - this.far = far || Infinity; - this.camera = null; - this.layers = new Layers(); - - this.params = { - Mesh: {}, - Line: { threshold: 1 }, - LOD: {}, - Points: { threshold: 1 }, - Sprite: {} - }; - - Object.defineProperties( this.params, { - PointCloud: { - get: function () { - - console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); - return this.Points; - - } - } - } ); - - } - - function ascSort( a, b ) { - - return a.distance - b.distance; - - } - - function intersectObject( object, raycaster, intersects, recursive ) { - - if ( object.layers.test( raycaster.layers ) ) { - - object.raycast( raycaster, intersects ); - - } - - if ( recursive === true ) { - - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { - - intersectObject( children[ i ], raycaster, intersects, true ); - - } - - } - - } - - Object.assign( Raycaster.prototype, { - - set: function ( origin, direction ) { - - // direction is assumed to be normalized (for accurate distance calculations) - - this.ray.set( origin, direction ); - - }, - - setFromCamera: function ( coords, camera ) { - - if ( ( camera && camera.isPerspectiveCamera ) ) { - - this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); - this.camera = camera; - - } else if ( ( camera && camera.isOrthographicCamera ) ) { - - this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera - this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); - this.camera = camera; - - } else { - - console.error( 'THREE.Raycaster: Unsupported camera type.' ); - - } - - }, - - intersectObject: function ( object, recursive, optionalTarget ) { - - var intersects = optionalTarget || []; - - intersectObject( object, this, intersects, recursive ); - - intersects.sort( ascSort ); - - return intersects; - - }, - - intersectObjects: function ( objects, recursive, optionalTarget ) { - - var intersects = optionalTarget || []; - - if ( Array.isArray( objects ) === false ) { - - console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); - return intersects; - - } - - for ( var i = 0, l = objects.length; i < l; i ++ ) { - - intersectObject( objects[ i ], this, intersects, recursive ); - - } - - intersects.sort( ascSort ); - - return intersects; - - } - - } ); - - /** - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - * - * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system - * - * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. - * The azimuthal angle (theta) is measured from the positive z-axis. - */ - - function Spherical( radius, phi, theta ) { - - this.radius = ( radius !== undefined ) ? radius : 1.0; - this.phi = ( phi !== undefined ) ? phi : 0; // polar angle - this.theta = ( theta !== undefined ) ? theta : 0; // azimuthal angle - - return this; - - } - - Object.assign( Spherical.prototype, { - - set: function ( radius, phi, theta ) { - - this.radius = radius; - this.phi = phi; - this.theta = theta; - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( other ) { - - this.radius = other.radius; - this.phi = other.phi; - this.theta = other.theta; - - return this; - - }, - - // restrict phi to be betwee EPS and PI-EPS - makeSafe: function () { - - var EPS = 0.000001; - this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); - - return this; - - }, - - setFromVector3: function ( v ) { - - return this.setFromCartesianCoords( v.x, v.y, v.z ); - - }, - - setFromCartesianCoords: function ( x, y, z ) { - - this.radius = Math.sqrt( x * x + y * y + z * z ); - - if ( this.radius === 0 ) { - - this.theta = 0; - this.phi = 0; - - } else { - - this.theta = Math.atan2( x, z ); - this.phi = Math.acos( MathUtils.clamp( y / this.radius, - 1, 1 ) ); - - } - - return this; - - } - - } ); - - /** - * @author Mugen87 / https://github.com/Mugen87 - * - * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system - * - */ - - function Cylindrical( radius, theta, y ) { - - this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane - this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis - this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane - - return this; - - } - - Object.assign( Cylindrical.prototype, { - - set: function ( radius, theta, y ) { - - this.radius = radius; - this.theta = theta; - this.y = y; - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( other ) { - - this.radius = other.radius; - this.theta = other.theta; - this.y = other.y; - - return this; - - }, - - setFromVector3: function ( v ) { - - return this.setFromCartesianCoords( v.x, v.y, v.z ); - - }, - - setFromCartesianCoords: function ( x, y, z ) { - - this.radius = Math.sqrt( x * x + z * z ); - this.theta = Math.atan2( x, z ); - this.y = y; - - return this; - - } - - } ); - - /** - * @author bhouston / http://clara.io - */ - - var _vector$7 = new Vector2(); - - function Box2( min, max ) { - - this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); - - } - - Object.assign( Box2.prototype, { - - set: function ( min, max ) { - - this.min.copy( min ); - this.max.copy( max ); - - return this; - - }, - - setFromPoints: function ( points ) { - - this.makeEmpty(); - - for ( var i = 0, il = points.length; i < il; i ++ ) { - - this.expandByPoint( points[ i ] ); - - } - - return this; - - }, - - setFromCenterAndSize: function ( center, size ) { - - var halfSize = _vector$7.copy( size ).multiplyScalar( 0.5 ); - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); - - return this; - - }, - - makeEmpty: function () { - - this.min.x = this.min.y = + Infinity; - this.max.x = this.max.y = - Infinity; - - return this; - - }, - - isEmpty: function () { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); - - }, - - getCenter: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .getCenter() target is now required' ); - target = new Vector2(); - - } - - return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - - }, - - getSize: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .getSize() target is now required' ); - target = new Vector2(); - - } - - return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); - - }, - - expandByPoint: function ( point ) { - - this.min.min( point ); - this.max.max( point ); - - return this; - - }, - - expandByVector: function ( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); - - return this; - - }, - - expandByScalar: function ( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); - - return this; - - }, - - containsPoint: function ( point ) { - - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y ? false : true; - - }, - - containsBox: function ( box ) { - - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y; - - }, - - getParameter: function ( point, target ) { - - // This can potentially have a divide by zero if the box - // has a size dimension of 0. - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .getParameter() target is now required' ); - target = new Vector2(); - - } - - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ) - ); - - }, - - intersectsBox: function ( box ) { - - // using 4 splitting planes to rule out intersections - - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y ? false : true; - - }, - - clampPoint: function ( point, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box2: .clampPoint() target is now required' ); - target = new Vector2(); - - } - - return target.copy( point ).clamp( this.min, this.max ); - - }, - - distanceToPoint: function ( point ) { - - var clampedPoint = _vector$7.copy( point ).clamp( this.min, this.max ); - return clampedPoint.sub( point ).length(); - - }, - - intersect: function ( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); - - return this; - - }, - - union: function ( box ) { - - this.min.min( box.min ); - this.max.max( box.max ); - - return this; - - }, - - translate: function ( offset ) { - - this.min.add( offset ); - this.max.add( offset ); - - return this; - - }, - - equals: function ( box ) { - - return box.min.equals( this.min ) && box.max.equals( this.max ); - - } - - } ); - - /** - * @author bhouston / http://clara.io - */ - - var _startP = new Vector3(); - var _startEnd = new Vector3(); - - function Line3( start, end ) { - - this.start = ( start !== undefined ) ? start : new Vector3(); - this.end = ( end !== undefined ) ? end : new Vector3(); - - } - - Object.assign( Line3.prototype, { - - set: function ( start, end ) { - - this.start.copy( start ); - this.end.copy( end ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( line ) { - - this.start.copy( line.start ); - this.end.copy( line.end ); - - return this; - - }, - - getCenter: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .getCenter() target is now required' ); - target = new Vector3(); - - } - - return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); - - }, - - delta: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .delta() target is now required' ); - target = new Vector3(); - - } - - return target.subVectors( this.end, this.start ); - - }, - - distanceSq: function () { - - return this.start.distanceToSquared( this.end ); - - }, - - distance: function () { - - return this.start.distanceTo( this.end ); - - }, - - at: function ( t, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .at() target is now required' ); - target = new Vector3(); - - } - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - }, - - closestPointToPointParameter: function ( point, clampToLine ) { - - _startP.subVectors( point, this.start ); - _startEnd.subVectors( this.end, this.start ); - - var startEnd2 = _startEnd.dot( _startEnd ); - var startEnd_startP = _startEnd.dot( _startP ); - - var t = startEnd_startP / startEnd2; - - if ( clampToLine ) { - - t = MathUtils.clamp( t, 0, 1 ); - - } - - return t; - - }, - - closestPointToPoint: function ( point, clampToLine, target ) { - - var t = this.closestPointToPointParameter( point, clampToLine ); - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - }, - - applyMatrix4: function ( matrix ) { - - this.start.applyMatrix4( matrix ); - this.end.applyMatrix4( matrix ); - - return this; - - }, - - equals: function ( line ) { - - return line.start.equals( this.start ) && line.end.equals( this.end ); - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function ImmediateRenderObject( material ) { - - Object3D.call( this ); - - this.material = material; - this.render = function ( /* renderCallback */ ) {}; - - this.hasPositions = false; - this.hasNormals = false; - this.hasColors = false; - this.hasUvs = false; - - this.positionArray = null; - this.normalArray = null; - this.colorArray = null; - this.uvArray = null; - - this.count = 0; - - } - - ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); - ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; - - ImmediateRenderObject.prototype.isImmediateRenderObject = true; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - var _vector$8 = new Vector3(); - - function SpotLightHelper( light, color ) { - - Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; - - this.color = color; - - var geometry = new BufferGeometry(); - - var positions = [ - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 1, 0, 1, - 0, 0, 0, - 1, 0, 1, - 0, 0, 0, 0, 1, 1, - 0, 0, 0, 0, - 1, 1 - ]; - - for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { - - var p1 = ( i / l ) * Math.PI * 2; - var p2 = ( j / l ) * Math.PI * 2; - - positions.push( - Math.cos( p1 ), Math.sin( p1 ), 1, - Math.cos( p2 ), Math.sin( p2 ), 1 - ); - - } - - geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - - var material = new LineBasicMaterial( { fog: false, toneMapped: false } ); - - this.cone = new LineSegments( geometry, material ); - this.add( this.cone ); - - this.update(); - - } - - SpotLightHelper.prototype = Object.create( Object3D.prototype ); - SpotLightHelper.prototype.constructor = SpotLightHelper; - - SpotLightHelper.prototype.dispose = function () { - - this.cone.geometry.dispose(); - this.cone.material.dispose(); - - }; - - SpotLightHelper.prototype.update = function () { - - this.light.updateMatrixWorld(); - - var coneLength = this.light.distance ? this.light.distance : 1000; - var coneWidth = coneLength * Math.tan( this.light.angle ); - - this.cone.scale.set( coneWidth, coneWidth, coneLength ); - - _vector$8.setFromMatrixPosition( this.light.target.matrixWorld ); - - this.cone.lookAt( _vector$8 ); - - if ( this.color !== undefined ) { - - this.cone.material.color.set( this.color ); - - } else { - - this.cone.material.color.copy( this.light.color ); - - } - - }; - - /** - * @author Sean Griffin / http://twitter.com/sgrif - * @author Michael Guerrero / http://realitymeltdown.com - * @author mrdoob / http://mrdoob.com/ - * @author ikerr / http://verold.com - * @author Mugen87 / https://github.com/Mugen87 - */ - - var _vector$9 = new Vector3(); - var _boneMatrix = new Matrix4(); - var _matrixWorldInv = new Matrix4(); - - function getBoneList( object ) { - - var boneList = []; - - if ( object && object.isBone ) { - - boneList.push( object ); - - } - - for ( var i = 0; i < object.children.length; i ++ ) { - - boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); - - } - - return boneList; - - } - - function SkeletonHelper( object ) { - - var bones = getBoneList( object ); - - var geometry = new BufferGeometry(); - - var vertices = []; - var colors = []; - - var color1 = new Color( 0, 0, 1 ); - var color2 = new Color( 0, 1, 0 ); - - for ( var i = 0; i < bones.length; i ++ ) { - - var bone = bones[ i ]; - - if ( bone.parent && bone.parent.isBone ) { - - vertices.push( 0, 0, 0 ); - vertices.push( 0, 0, 0 ); - colors.push( color1.r, color1.g, color1.b ); - colors.push( color2.r, color2.g, color2.b ); - - } - - } - - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - var material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); - - LineSegments.call( this, geometry, material ); - - this.type = 'SkeletonHelper'; - - this.root = object; - this.bones = bones; - - this.matrix = object.matrixWorld; - this.matrixAutoUpdate = false; - - } - - SkeletonHelper.prototype = Object.create( LineSegments.prototype ); - SkeletonHelper.prototype.constructor = SkeletonHelper; - - SkeletonHelper.prototype.isSkeletonHelper = true; - - SkeletonHelper.prototype.updateMatrixWorld = function ( force ) { - - var bones = this.bones; - - var geometry = this.geometry; - var position = geometry.getAttribute( 'position' ); - - _matrixWorldInv.getInverse( this.root.matrixWorld ); - - for ( var i = 0, j = 0; i < bones.length; i ++ ) { - - var bone = bones[ i ]; - - if ( bone.parent && bone.parent.isBone ) { - - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); - _vector$9.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j, _vector$9.x, _vector$9.y, _vector$9.z ); - - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); - _vector$9.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j + 1, _vector$9.x, _vector$9.y, _vector$9.z ); - - j += 2; - - } - - } - - geometry.getAttribute( 'position' ).needsUpdate = true; - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - - function PointLightHelper( light, sphereSize, color ) { - - this.light = light; - this.light.updateMatrixWorld(); - - this.color = color; - - var geometry = new SphereBufferGeometry( sphereSize, 4, 2 ); - var material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); - - Mesh.call( this, geometry, material ); - - this.type = 'PointLightHelper'; - - this.matrix = this.light.matrixWorld; - this.matrixAutoUpdate = false; - - this.update(); - - - /* - var distanceGeometry = new THREE.IcosahedronBufferGeometry( 1, 2 ); - var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); - - this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); - this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); - - var d = light.distance; - - if ( d === 0.0 ) { - - this.lightDistance.visible = false; - - } else { - - this.lightDistance.scale.set( d, d, d ); - - } - - this.add( this.lightDistance ); - */ - - } - - PointLightHelper.prototype = Object.create( Mesh.prototype ); - PointLightHelper.prototype.constructor = PointLightHelper; - - PointLightHelper.prototype.dispose = function () { - - this.geometry.dispose(); - this.material.dispose(); - - }; - - PointLightHelper.prototype.update = function () { - - if ( this.color !== undefined ) { - - this.material.color.set( this.color ); - - } else { - - this.material.color.copy( this.light.color ); - - } - - /* - var d = this.light.distance; - - if ( d === 0.0 ) { - - this.lightDistance.visible = false; - - } else { - - this.lightDistance.visible = true; - this.lightDistance.scale.set( d, d, d ); - - } - */ - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ - - var _vector$a = new Vector3(); - var _color1 = new Color(); - var _color2 = new Color(); - - function HemisphereLightHelper( light, size, color ) { - - Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; - - this.color = color; - - var geometry = new OctahedronBufferGeometry( size ); - geometry.rotateY( Math.PI * 0.5 ); - - this.material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); - if ( this.color === undefined ) { this.material.vertexColors = true; } - - var position = geometry.getAttribute( 'position' ); - var colors = new Float32Array( position.count * 3 ); - - geometry.setAttribute( 'color', new BufferAttribute( colors, 3 ) ); - - this.add( new Mesh( geometry, this.material ) ); - - this.update(); - - } - - HemisphereLightHelper.prototype = Object.create( Object3D.prototype ); - HemisphereLightHelper.prototype.constructor = HemisphereLightHelper; - - HemisphereLightHelper.prototype.dispose = function () { - - this.children[ 0 ].geometry.dispose(); - this.children[ 0 ].material.dispose(); - - }; - - HemisphereLightHelper.prototype.update = function () { - - var mesh = this.children[ 0 ]; - - if ( this.color !== undefined ) { - - this.material.color.set( this.color ); - - } else { - - var colors = mesh.geometry.getAttribute( 'color' ); - - _color1.copy( this.light.color ); - _color2.copy( this.light.groundColor ); - - for ( var i = 0, l = colors.count; i < l; i ++ ) { - - var color = ( i < ( l / 2 ) ) ? _color1 : _color2; - - colors.setXYZ( i, color.r, color.g, color.b ); - - } - - colors.needsUpdate = true; - - } - - mesh.lookAt( _vector$a.setFromMatrixPosition( this.light.matrixWorld ).negate() ); - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function GridHelper( size, divisions, color1, color2 ) { - - size = size || 10; - divisions = divisions || 10; - color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); - color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); - - var center = divisions / 2; - var step = size / divisions; - var halfSize = size / 2; - - var vertices = [], colors = []; - - for ( var i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { - - vertices.push( - halfSize, 0, k, halfSize, 0, k ); - vertices.push( k, 0, - halfSize, k, 0, halfSize ); - - var color = i === center ? color1 : color2; - - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - - } - - var geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - var material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - - LineSegments.call( this, geometry, material ); - - this.type = 'GridHelper'; - - } - - GridHelper.prototype = Object.assign( Object.create( LineSegments.prototype ), { - - constructor: GridHelper, - - copy: function ( source ) { - - LineSegments.prototype.copy.call( this, source ); - - this.geometry.copy( source.geometry ); - this.material.copy( source.material ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / http://github.com/Mugen87 - * @author Hectate / http://www.github.com/Hectate - */ - - function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) { - - radius = radius || 10; - radials = radials || 16; - circles = circles || 8; - divisions = divisions || 64; - color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); - color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); - - var vertices = []; - var colors = []; - - var x, z; - var v, i, j, r, color; - - // create the radials - - for ( i = 0; i <= radials; i ++ ) { - - v = ( i / radials ) * ( Math.PI * 2 ); - - x = Math.sin( v ) * radius; - z = Math.cos( v ) * radius; - - vertices.push( 0, 0, 0 ); - vertices.push( x, 0, z ); - - color = ( i & 1 ) ? color1 : color2; - - colors.push( color.r, color.g, color.b ); - colors.push( color.r, color.g, color.b ); - - } - - // create the circles - - for ( i = 0; i <= circles; i ++ ) { - - color = ( i & 1 ) ? color1 : color2; - - r = radius - ( radius / circles * i ); - - for ( j = 0; j < divisions; j ++ ) { - - // first vertex - - v = ( j / divisions ) * ( Math.PI * 2 ); - - x = Math.sin( v ) * r; - z = Math.cos( v ) * r; - - vertices.push( x, 0, z ); - colors.push( color.r, color.g, color.b ); - - // second vertex - - v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); - - x = Math.sin( v ) * r; - z = Math.cos( v ) * r; - - vertices.push( x, 0, z ); - colors.push( color.r, color.g, color.b ); - - } - - } - - var geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - var material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - - LineSegments.call( this, geometry, material ); - - this.type = 'PolarGridHelper'; - - } - - PolarGridHelper.prototype = Object.create( LineSegments.prototype ); - PolarGridHelper.prototype.constructor = PolarGridHelper; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - var _v1$5 = new Vector3(); - var _v2$3 = new Vector3(); - var _v3$1 = new Vector3(); - - function DirectionalLightHelper( light, size, color ) { - - Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; - - this.color = color; - - if ( size === undefined ) { size = 1; } - - var geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new Float32BufferAttribute( [ - - size, size, 0, - size, size, 0, - size, - size, 0, - - size, - size, 0, - - size, size, 0 - ], 3 ) ); - - var material = new LineBasicMaterial( { fog: false, toneMapped: false } ); - - this.lightPlane = new Line( geometry, material ); - this.add( this.lightPlane ); - - geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); - - this.targetLine = new Line( geometry, material ); - this.add( this.targetLine ); - - this.update(); - - } - - DirectionalLightHelper.prototype = Object.create( Object3D.prototype ); - DirectionalLightHelper.prototype.constructor = DirectionalLightHelper; - - DirectionalLightHelper.prototype.dispose = function () { - - this.lightPlane.geometry.dispose(); - this.lightPlane.material.dispose(); - this.targetLine.geometry.dispose(); - this.targetLine.material.dispose(); - - }; - - DirectionalLightHelper.prototype.update = function () { - - _v1$5.setFromMatrixPosition( this.light.matrixWorld ); - _v2$3.setFromMatrixPosition( this.light.target.matrixWorld ); - _v3$1.subVectors( _v2$3, _v1$5 ); - - this.lightPlane.lookAt( _v2$3 ); - - if ( this.color !== undefined ) { - - this.lightPlane.material.color.set( this.color ); - this.targetLine.material.color.set( this.color ); - - } else { - - this.lightPlane.material.color.copy( this.light.color ); - this.targetLine.material.color.copy( this.light.color ); - - } - - this.targetLine.lookAt( _v2$3 ); - this.targetLine.scale.z = _v3$1.length(); - - }; - - /** - * @author alteredq / http://alteredqualia.com/ - * @author Mugen87 / https://github.com/Mugen87 - * - * - shows frustum, line of sight and up of the camera - * - suitable for fast updates - * - based on frustum visualization in lightgl.js shadowmap example - * http://evanw.github.com/lightgl.js/tests/shadowmap.html - */ - - var _vector$b = new Vector3(); - var _camera = new Camera(); - - function CameraHelper( camera ) { - - var geometry = new BufferGeometry(); - var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: true, toneMapped: false } ); - - var vertices = []; - var colors = []; - - var pointMap = {}; - - // colors - - var colorFrustum = new Color( 0xffaa00 ); - var colorCone = new Color( 0xff0000 ); - var colorUp = new Color( 0x00aaff ); - var colorTarget = new Color( 0xffffff ); - var colorCross = new Color( 0x333333 ); - - // near - - addLine( 'n1', 'n2', colorFrustum ); - addLine( 'n2', 'n4', colorFrustum ); - addLine( 'n4', 'n3', colorFrustum ); - addLine( 'n3', 'n1', colorFrustum ); - - // far - - addLine( 'f1', 'f2', colorFrustum ); - addLine( 'f2', 'f4', colorFrustum ); - addLine( 'f4', 'f3', colorFrustum ); - addLine( 'f3', 'f1', colorFrustum ); - - // sides - - addLine( 'n1', 'f1', colorFrustum ); - addLine( 'n2', 'f2', colorFrustum ); - addLine( 'n3', 'f3', colorFrustum ); - addLine( 'n4', 'f4', colorFrustum ); - - // cone - - addLine( 'p', 'n1', colorCone ); - addLine( 'p', 'n2', colorCone ); - addLine( 'p', 'n3', colorCone ); - addLine( 'p', 'n4', colorCone ); - - // up - - addLine( 'u1', 'u2', colorUp ); - addLine( 'u2', 'u3', colorUp ); - addLine( 'u3', 'u1', colorUp ); - - // target - - addLine( 'c', 't', colorTarget ); - addLine( 'p', 'c', colorCross ); - - // cross - - addLine( 'cn1', 'cn2', colorCross ); - addLine( 'cn3', 'cn4', colorCross ); - - addLine( 'cf1', 'cf2', colorCross ); - addLine( 'cf3', 'cf4', colorCross ); - - function addLine( a, b, color ) { - - addPoint( a, color ); - addPoint( b, color ); - - } - - function addPoint( id, color ) { - - vertices.push( 0, 0, 0 ); - colors.push( color.r, color.g, color.b ); - - if ( pointMap[ id ] === undefined ) { - - pointMap[ id ] = []; - - } - - pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); - - } - - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - LineSegments.call( this, geometry, material ); - - this.type = 'CameraHelper'; - - this.camera = camera; - if ( this.camera.updateProjectionMatrix ) { this.camera.updateProjectionMatrix(); } - - this.matrix = camera.matrixWorld; - this.matrixAutoUpdate = false; - - this.pointMap = pointMap; - - this.update(); - - } - - CameraHelper.prototype = Object.create( LineSegments.prototype ); - CameraHelper.prototype.constructor = CameraHelper; - - CameraHelper.prototype.update = function () { - - var geometry = this.geometry; - var pointMap = this.pointMap; - - var w = 1, h = 1; - - // we need just camera projection matrix inverse - // world matrix must be identity - - _camera.projectionMatrixInverse.copy( this.camera.projectionMatrixInverse ); - - // center / target - - setPoint( 'c', pointMap, geometry, _camera, 0, 0, - 1 ); - setPoint( 't', pointMap, geometry, _camera, 0, 0, 1 ); - - // near - - setPoint( 'n1', pointMap, geometry, _camera, - w, - h, - 1 ); - setPoint( 'n2', pointMap, geometry, _camera, w, - h, - 1 ); - setPoint( 'n3', pointMap, geometry, _camera, - w, h, - 1 ); - setPoint( 'n4', pointMap, geometry, _camera, w, h, - 1 ); - - // far - - setPoint( 'f1', pointMap, geometry, _camera, - w, - h, 1 ); - setPoint( 'f2', pointMap, geometry, _camera, w, - h, 1 ); - setPoint( 'f3', pointMap, geometry, _camera, - w, h, 1 ); - setPoint( 'f4', pointMap, geometry, _camera, w, h, 1 ); - - // up - - setPoint( 'u1', pointMap, geometry, _camera, w * 0.7, h * 1.1, - 1 ); - setPoint( 'u2', pointMap, geometry, _camera, - w * 0.7, h * 1.1, - 1 ); - setPoint( 'u3', pointMap, geometry, _camera, 0, h * 2, - 1 ); - - // cross - - setPoint( 'cf1', pointMap, geometry, _camera, - w, 0, 1 ); - setPoint( 'cf2', pointMap, geometry, _camera, w, 0, 1 ); - setPoint( 'cf3', pointMap, geometry, _camera, 0, - h, 1 ); - setPoint( 'cf4', pointMap, geometry, _camera, 0, h, 1 ); - - setPoint( 'cn1', pointMap, geometry, _camera, - w, 0, - 1 ); - setPoint( 'cn2', pointMap, geometry, _camera, w, 0, - 1 ); - setPoint( 'cn3', pointMap, geometry, _camera, 0, - h, - 1 ); - setPoint( 'cn4', pointMap, geometry, _camera, 0, h, - 1 ); - - geometry.getAttribute( 'position' ).needsUpdate = true; - - }; - - function setPoint( point, pointMap, geometry, camera, x, y, z ) { - - _vector$b.set( x, y, z ).unproject( camera ); - - var points = pointMap[ point ]; - - if ( points !== undefined ) { - - var position = geometry.getAttribute( 'position' ); - - for ( var i = 0, l = points.length; i < l; i ++ ) { - - position.setXYZ( points[ i ], _vector$b.x, _vector$b.y, _vector$b.z ); - - } - - } - - } - - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / http://github.com/Mugen87 - */ - - var _box$3 = new Box3(); - - function BoxHelper( object, color ) { - - this.object = object; - - if ( color === undefined ) { color = 0xffff00; } - - var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); - var positions = new Float32Array( 8 * 3 ); - - var geometry = new BufferGeometry(); - geometry.setIndex( new BufferAttribute( indices, 1 ) ); - geometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) ); - - LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); - - this.type = 'BoxHelper'; - - this.matrixAutoUpdate = false; - - this.update(); - - } - - BoxHelper.prototype = Object.create( LineSegments.prototype ); - BoxHelper.prototype.constructor = BoxHelper; - - BoxHelper.prototype.update = function ( object ) { - - if ( object !== undefined ) { - - console.warn( 'THREE.BoxHelper: .update() has no longer arguments.' ); - - } - - if ( this.object !== undefined ) { - - _box$3.setFromObject( this.object ); - - } - - if ( _box$3.isEmpty() ) { return; } - - var min = _box$3.min; - var max = _box$3.max; - - /* - 5____4 - 1/___0/| - | 6__|_7 - 2/___3/ - - 0: max.x, max.y, max.z - 1: min.x, max.y, max.z - 2: min.x, min.y, max.z - 3: max.x, min.y, max.z - 4: max.x, max.y, min.z - 5: min.x, max.y, min.z - 6: min.x, min.y, min.z - 7: max.x, min.y, min.z - */ - - var position = this.geometry.attributes.position; - var array = position.array; - - array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; - array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; - array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; - array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; - array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; - array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; - array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; - array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; - - position.needsUpdate = true; - - this.geometry.computeBoundingSphere(); - - - }; - - BoxHelper.prototype.setFromObject = function ( object ) { - - this.object = object; - this.update(); - - return this; - - }; - - BoxHelper.prototype.copy = function ( source ) { - - LineSegments.prototype.copy.call( this, source ); - - this.object = source.object; - - return this; - - }; - - BoxHelper.prototype.clone = function () { - - return new this.constructor().copy( this ); - - }; - - /** - * @author WestLangley / http://github.com/WestLangley - */ - - function Box3Helper( box, color ) { - - this.type = 'Box3Helper'; - - this.box = box; - - color = color || 0xffff00; - - var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); - - var positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; - - var geometry = new BufferGeometry(); - - geometry.setIndex( new BufferAttribute( indices, 1 ) ); - - geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - - LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); - - this.type = 'Box3Helper'; - - this.geometry.computeBoundingSphere(); - - } - - Box3Helper.prototype = Object.create( LineSegments.prototype ); - Box3Helper.prototype.constructor = Box3Helper; - - Box3Helper.prototype.updateMatrixWorld = function ( force ) { - - var box = this.box; - - if ( box.isEmpty() ) { return; } - - box.getCenter( this.position ); - - box.getSize( this.scale ); - - this.scale.multiplyScalar( 0.5 ); - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - }; - - /** - * @author WestLangley / http://github.com/WestLangley - */ - - function PlaneHelper( plane, size, hex ) { - - this.plane = plane; - - this.size = ( size === undefined ) ? 1 : size; - - var color = ( hex !== undefined ) ? hex : 0xffff00; - - var positions = [ 1, - 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 ]; - - var geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - geometry.computeBoundingSphere(); - - Line.call( this, geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); - - this.type = 'PlaneHelper'; - - // - - var positions2 = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, - 1, 1, 1, - 1, 1 ]; - - var geometry2 = new BufferGeometry(); - geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) ); - geometry2.computeBoundingSphere(); - - this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false } ) ) ); - - } - - PlaneHelper.prototype = Object.create( Line.prototype ); - PlaneHelper.prototype.constructor = PlaneHelper; - - PlaneHelper.prototype.updateMatrixWorld = function ( force ) { - - var scale = - this.plane.constant; - - if ( Math.abs( scale ) < 1e-8 ) { scale = 1e-8; } // sign does not matter - - this.scale.set( 0.5 * this.size, 0.5 * this.size, scale ); - - this.children[ 0 ].material.side = ( scale < 0 ) ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here - - this.lookAt( this.plane.normal ); - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - }; - - /** - * @author WestLangley / http://github.com/WestLangley - * @author zz85 / http://github.com/zz85 - * @author bhouston / http://clara.io - * - * Creates an arrow for visualizing directions - * - * Parameters: - * dir - Vector3 - * origin - Vector3 - * length - Number - * color - color in hex value - * headLength - Number - * headWidth - Number - */ - - var _axis = new Vector3(); - var _lineGeometry, _coneGeometry; - - function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { - - // dir is assumed to be normalized - - Object3D.call( this ); - - this.type = 'ArrowHelper'; - - if ( dir === undefined ) { dir = new Vector3( 0, 0, 1 ); } - if ( origin === undefined ) { origin = new Vector3( 0, 0, 0 ); } - if ( length === undefined ) { length = 1; } - if ( color === undefined ) { color = 0xffff00; } - if ( headLength === undefined ) { headLength = 0.2 * length; } - if ( headWidth === undefined ) { headWidth = 0.2 * headLength; } - - if ( _lineGeometry === undefined ) { - - _lineGeometry = new BufferGeometry(); - _lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); - - _coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); - _coneGeometry.translate( 0, - 0.5, 0 ); - - } - - this.position.copy( origin ); - - this.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); - this.line.matrixAutoUpdate = false; - this.add( this.line ); - - this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color, toneMapped: false } ) ); - this.cone.matrixAutoUpdate = false; - this.add( this.cone ); - - this.setDirection( dir ); - this.setLength( length, headLength, headWidth ); - - } - - ArrowHelper.prototype = Object.create( Object3D.prototype ); - ArrowHelper.prototype.constructor = ArrowHelper; - - ArrowHelper.prototype.setDirection = function ( dir ) { - - // dir is assumed to be normalized - - if ( dir.y > 0.99999 ) { - - this.quaternion.set( 0, 0, 0, 1 ); - - } else if ( dir.y < - 0.99999 ) { - - this.quaternion.set( 1, 0, 0, 0 ); - - } else { - - _axis.set( dir.z, 0, - dir.x ).normalize(); - - var radians = Math.acos( dir.y ); - - this.quaternion.setFromAxisAngle( _axis, radians ); - - } - - }; - - ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { - - if ( headLength === undefined ) { headLength = 0.2 * length; } - if ( headWidth === undefined ) { headWidth = 0.2 * headLength; } - - this.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458 - this.line.updateMatrix(); - - this.cone.scale.set( headWidth, headLength, headWidth ); - this.cone.position.y = length; - this.cone.updateMatrix(); - - }; - - ArrowHelper.prototype.setColor = function ( color ) { - - this.line.material.color.set( color ); - this.cone.material.color.set( color ); - - }; - - ArrowHelper.prototype.copy = function ( source ) { - - Object3D.prototype.copy.call( this, source, false ); - - this.line.copy( source.line ); - this.cone.copy( source.cone ); - - return this; - - }; - - ArrowHelper.prototype.clone = function () { - - return new this.constructor().copy( this ); - - }; - - /** - * @author sroucheray / http://sroucheray.org/ - * @author mrdoob / http://mrdoob.com/ - */ - - function AxesHelper( size ) { - - size = size || 1; - - var vertices = [ - 0, 0, 0, size, 0, 0, - 0, 0, 0, 0, size, 0, - 0, 0, 0, 0, 0, size - ]; - - var colors = [ - 1, 0, 0, 1, 0.6, 0, - 0, 1, 0, 0.6, 1, 0, - 0, 0, 1, 0, 0.6, 1 - ]; - - var geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - - var material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - - LineSegments.call( this, geometry, material ); - - this.type = 'AxesHelper'; - - } - - AxesHelper.prototype = Object.create( LineSegments.prototype ); - AxesHelper.prototype.constructor = AxesHelper; - - /** - * @author Emmett Lalish / elalish - * - * This class generates a Prefiltered, Mipmapped Radiance Environment Map - * (PMREM) from a cubeMap environment texture. This allows different levels of - * blur to be quickly accessed based on material roughness. It is packed into a - * special CubeUV format that allows us to perform custom interpolation so that - * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap - * chain, it only goes down to the LOD_MIN level (above), and then creates extra - * even more filtered 'mips' at the same LOD_MIN resolution, associated with - * higher roughness levels. In this way we maintain resolution to smoothly - * interpolate diffuse lighting while limiting sampling computation. - */ - - var LOD_MIN = 4; - var LOD_MAX = 8; - var SIZE_MAX = Math.pow( 2, LOD_MAX ); - - // The standard deviations (radians) associated with the extra mips. These are - // chosen to approximate a Trowbridge-Reitz distribution function times the - // geometric shadowing function. These sigma values squared must match the - // variance #defines in cube_uv_reflection_fragment.glsl.js. - var EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; - - var TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; - - // The maximum length of the blur for loop. Smaller sigmas will use fewer - // samples and exit early, but not recompile the shader. - var MAX_SAMPLES = 20; - - var ENCODINGS = {}; - ENCODINGS[ LinearEncoding ] = 0; - ENCODINGS[ sRGBEncoding ] = 1; - ENCODINGS[ RGBEEncoding ] = 2; - ENCODINGS[ RGBM7Encoding ] = 3; - ENCODINGS[ RGBM16Encoding ] = 4; - ENCODINGS[ RGBDEncoding ] = 5; - ENCODINGS[ GammaEncoding ] = 6; - - var _flatCamera = new OrthographicCamera(); - var ref = _createPlanes(); - var _lodPlanes = ref._lodPlanes; - var _sizeLods = ref._sizeLods; - var _sigmas = ref._sigmas; - var _oldTarget = null; - - // Golden Ratio - var PHI = ( 1 + Math.sqrt( 5 ) ) / 2; - var INV_PHI = 1 / PHI; - - // Vertices of a dodecahedron (except the opposites, which represent the - // same axis), used as axis directions evenly spread on a sphere. - var _axisDirections = [ - new Vector3( 1, 1, 1 ), - new Vector3( - 1, 1, 1 ), - new Vector3( 1, 1, - 1 ), - new Vector3( - 1, 1, - 1 ), - new Vector3( 0, PHI, INV_PHI ), - new Vector3( 0, PHI, - INV_PHI ), - new Vector3( INV_PHI, 0, PHI ), - new Vector3( - INV_PHI, 0, PHI ), - new Vector3( PHI, INV_PHI, 0 ), - new Vector3( - PHI, INV_PHI, 0 ) ]; - - function PMREMGenerator( renderer ) { - - this._renderer = renderer; - this._pingPongRenderTarget = null; - - this._blurMaterial = _getBlurShader( MAX_SAMPLES ); - this._equirectShader = null; - this._cubemapShader = null; - - this._compileMaterial( this._blurMaterial ); - - } - - PMREMGenerator.prototype = { - - constructor: PMREMGenerator, - - /** - * Generates a PMREM from a supplied Scene, which can be faster than using an - * image if networking bandwidth is low. Optional sigma specifies a blur radius - * in radians to be applied to the scene before PMREM generation. Optional near - * and far planes ensure the scene is rendered in its entirety (the cubeCamera - * is placed at the origin). - */ - fromScene: function ( scene, sigma, near, far ) { - if ( sigma === void 0 ) sigma = 0; - if ( near === void 0 ) near = 0.1; - if ( far === void 0 ) far = 100; - - - _oldTarget = this._renderer.getRenderTarget(); - var cubeUVRenderTarget = this._allocateTargets(); - - this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); - if ( sigma > 0 ) { - - this._blur( cubeUVRenderTarget, 0, 0, sigma ); - - } - - this._applyPMREM( cubeUVRenderTarget ); - this._cleanup( cubeUVRenderTarget ); - - return cubeUVRenderTarget; - - }, - - /** - * Generates a PMREM from an equirectangular texture, which can be either LDR - * (RGBFormat) or HDR (RGBEFormat). The ideal input image size is 1k (1024 x 512), - * as this matches best with the 256 x 256 cubemap output. - */ - fromEquirectangular: function ( equirectangular ) { - - equirectangular.magFilter = NearestFilter; - equirectangular.minFilter = NearestFilter; - equirectangular.generateMipmaps = false; - - return this.fromCubemap( equirectangular ); - - }, - - /** - * Generates a PMREM from an cubemap texture, which can be either LDR - * (RGBFormat) or HDR (RGBEFormat). The ideal input cube size is 256 x 256, - * as this matches best with the 256 x 256 cubemap output. - */ - fromCubemap: function ( cubemap ) { - - _oldTarget = this._renderer.getRenderTarget(); - var cubeUVRenderTarget = this._allocateTargets( cubemap ); - this._textureToCubeUV( cubemap, cubeUVRenderTarget ); - this._applyPMREM( cubeUVRenderTarget ); - this._cleanup( cubeUVRenderTarget ); - - return cubeUVRenderTarget; - - }, - - /** - * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during - * your texture's network fetch for increased concurrency. - */ - compileCubemapShader: function () { - - if ( this._cubemapShader === null ) { - - this._cubemapShader = _getCubemapShader(); - this._compileMaterial( this._cubemapShader ); - - } - - }, - - /** - * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during - * your texture's network fetch for increased concurrency. - */ - compileEquirectangularShader: function () { - - if ( this._equirectShader === null ) { - - this._equirectShader = _getEquirectShader(); - this._compileMaterial( this._equirectShader ); - - } - - }, - - /** - * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class, - * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on - * one of them will cause any others to also become unusable. - */ - dispose: function () { - - this._blurMaterial.dispose(); - - if ( this._cubemapShader !== null ) { this._cubemapShader.dispose(); } - if ( this._equirectShader !== null ) { this._equirectShader.dispose(); } - - for ( var i = 0; i < _lodPlanes.length; i ++ ) { - - _lodPlanes[ i ].dispose(); - - } - - }, - - // private interface - - _cleanup: function ( outputTarget ) { - - this._pingPongRenderTarget.dispose(); - this._renderer.setRenderTarget( _oldTarget ); - outputTarget.scissorTest = false; - // reset viewport and scissor - outputTarget.setSize( outputTarget.width, outputTarget.height ); - - }, - - _allocateTargets: function ( equirectangular ) { - - var params = { - magFilter: NearestFilter, - minFilter: NearestFilter, - generateMipmaps: false, - type: UnsignedByteType, - format: RGBEFormat, - encoding: _isLDR( equirectangular ) ? equirectangular.encoding : RGBEEncoding, - depthBuffer: false, - stencilBuffer: false - }; - - var cubeUVRenderTarget = _createRenderTarget( params ); - cubeUVRenderTarget.depthBuffer = equirectangular ? false : true; - this._pingPongRenderTarget = _createRenderTarget( params ); - return cubeUVRenderTarget; - - }, - - _compileMaterial: function ( material ) { - - var tmpMesh = new Mesh( _lodPlanes[ 0 ], material ); - this._renderer.compile( tmpMesh, _flatCamera ); - - }, - - _sceneToCubeUV: function ( scene, near, far, cubeUVRenderTarget ) { - - var fov = 90; - var aspect = 1; - var cubeCamera = new PerspectiveCamera( fov, aspect, near, far ); - var upSign = [ 1, - 1, 1, 1, 1, 1 ]; - var forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; - var renderer = this._renderer; - - var outputEncoding = renderer.outputEncoding; - var toneMapping = renderer.toneMapping; - var toneMappingExposure = renderer.toneMappingExposure; - var clearColor = renderer.getClearColor(); - var clearAlpha = renderer.getClearAlpha(); - - renderer.toneMapping = LinearToneMapping; - renderer.toneMappingExposure = 1.0; - renderer.outputEncoding = LinearEncoding; - - var background = scene.background; - if ( background && background.isColor ) { - - background.convertSRGBToLinear(); - // Convert linear to RGBE - var maxComponent = Math.max( background.r, background.g, background.b ); - var fExp = Math.min( Math.max( Math.ceil( Math.log2( maxComponent ) ), - 128.0 ), 127.0 ); - background = background.multiplyScalar( Math.pow( 2.0, - fExp ) ); - var alpha = ( fExp + 128.0 ) / 255.0; - renderer.setClearColor( background, alpha ); - scene.background = null; - - } - - for ( var i = 0; i < 6; i ++ ) { - - var col = i % 3; - if ( col == 0 ) { - - cubeCamera.up.set( 0, upSign[ i ], 0 ); - cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); - - } else if ( col == 1 ) { - - cubeCamera.up.set( 0, 0, upSign[ i ] ); - cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); - - } else { - - cubeCamera.up.set( 0, upSign[ i ], 0 ); - cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); - - } - - _setViewport( cubeUVRenderTarget, - col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX ); - renderer.setRenderTarget( cubeUVRenderTarget ); - renderer.render( scene, cubeCamera ); - - } - - renderer.toneMapping = toneMapping; - renderer.toneMappingExposure = toneMappingExposure; - renderer.outputEncoding = outputEncoding; - renderer.setClearColor( clearColor, clearAlpha ); - - }, - - _textureToCubeUV: function ( texture, cubeUVRenderTarget ) { - - var renderer = this._renderer; - - if ( texture.isCubeTexture ) { - - if ( this._cubemapShader == null ) { - - this._cubemapShader = _getCubemapShader(); - - } - - } else { - - if ( this._equirectShader == null ) { - - this._equirectShader = _getEquirectShader(); - - } - - } - - var material = texture.isCubeTexture ? this._cubemapShader : this._equirectShader; - var mesh = new Mesh( _lodPlanes[ 0 ], material ); - - var uniforms = material.uniforms; - - uniforms[ 'envMap' ].value = texture; - - if ( ! texture.isCubeTexture ) { - - uniforms[ 'texelSize' ].value.set( 1.0 / texture.image.width, 1.0 / texture.image.height ); - - } - - uniforms[ 'inputEncoding' ].value = ENCODINGS[ texture.encoding ]; - uniforms[ 'outputEncoding' ].value = ENCODINGS[ cubeUVRenderTarget.texture.encoding ]; - - _setViewport( cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX ); - - renderer.setRenderTarget( cubeUVRenderTarget ); - renderer.render( mesh, _flatCamera ); - - }, - - _applyPMREM: function ( cubeUVRenderTarget ) { - - var renderer = this._renderer; - var autoClear = renderer.autoClear; - renderer.autoClear = false; - - for ( var i = 1; i < TOTAL_LODS; i ++ ) { - - var sigma = Math.sqrt( _sigmas[ i ] * _sigmas[ i ] - _sigmas[ i - 1 ] * _sigmas[ i - 1 ] ); - - var poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; - - this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); - - } - - renderer.autoClear = autoClear; - - }, - - /** - * This is a two-pass Gaussian blur for a cubemap. Normally this is done - * vertically and horizontally, but this breaks down on a cube. Here we apply - * the blur latitudinally (around the poles), and then longitudinally (towards - * the poles) to approximate the orthogonally-separable blur. It is least - * accurate at the poles, but still does a decent job. - */ - _blur: function ( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { - - var pingPongRenderTarget = this._pingPongRenderTarget; - - this._halfBlur( - cubeUVRenderTarget, - pingPongRenderTarget, - lodIn, - lodOut, - sigma, - 'latitudinal', - poleAxis ); - - this._halfBlur( - pingPongRenderTarget, - cubeUVRenderTarget, - lodOut, - lodOut, - sigma, - 'longitudinal', - poleAxis ); - - }, - - _halfBlur: function ( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { - - var renderer = this._renderer; - var blurMaterial = this._blurMaterial; - - if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) { - - console.error( - 'blur direction must be either latitudinal or longitudinal!' ); - - } - - // Number of standard deviations at which to cut off the discrete approximation. - var STANDARD_DEVIATIONS = 3; - - var blurMesh = new Mesh( _lodPlanes[ lodOut ], blurMaterial ); - var blurUniforms = blurMaterial.uniforms; - - var pixels = _sizeLods[ lodIn ] - 1; - var radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); - var sigmaPixels = sigmaRadians / radiansPerPixel; - var samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; - - if ( samples > MAX_SAMPLES ) { - - console.warn( ("sigmaRadians, " + sigmaRadians + ", is too large and will clip, as it requested " + samples + " samples when the maximum is set to " + MAX_SAMPLES) ); - - } - - var weights = []; - var sum = 0; - - for ( var i = 0; i < MAX_SAMPLES; ++ i ) { - - var x = i / sigmaPixels; - var weight = Math.exp( - x * x / 2 ); - weights.push( weight ); - - if ( i == 0 ) { - - sum += weight; - - } else if ( i < samples ) { - - sum += 2 * weight; - - } - - } - - for ( var i = 0; i < weights.length; i ++ ) { - - weights[ i ] = weights[ i ] / sum; - - } - - blurUniforms[ 'envMap' ].value = targetIn.texture; - blurUniforms[ 'samples' ].value = samples; - blurUniforms[ 'weights' ].value = weights; - blurUniforms[ 'latitudinal' ].value = direction === 'latitudinal'; - - if ( poleAxis ) { - - blurUniforms[ 'poleAxis' ].value = poleAxis; - - } - - blurUniforms[ 'dTheta' ].value = radiansPerPixel; - blurUniforms[ 'mipInt' ].value = LOD_MAX - lodIn; - blurUniforms[ 'inputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ]; - blurUniforms[ 'outputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ]; - - var outputSize = _sizeLods[ lodOut ]; - var x = 3 * Math.max( 0, SIZE_MAX - 2 * outputSize ); - var y = ( lodOut === 0 ? 0 : 2 * SIZE_MAX ) + 2 * outputSize * ( lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0 ); - - _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); - renderer.setRenderTarget( targetOut ); - renderer.render( blurMesh, _flatCamera ); - - } - - }; - - function _isLDR( texture ) { - - if ( texture === undefined || texture.type !== UnsignedByteType ) { return false; } - - return texture.encoding === LinearEncoding || texture.encoding === sRGBEncoding || texture.encoding === GammaEncoding; - - } - - function _createPlanes() { - - var _lodPlanes = []; - var _sizeLods = []; - var _sigmas = []; - - var lod = LOD_MAX; - - for ( var i = 0; i < TOTAL_LODS; i ++ ) { - - var sizeLod = Math.pow( 2, lod ); - _sizeLods.push( sizeLod ); - var sigma = 1.0 / sizeLod; - - if ( i > LOD_MAX - LOD_MIN ) { - - sigma = EXTRA_LOD_SIGMA[ i - LOD_MAX + LOD_MIN - 1 ]; - - } else if ( i == 0 ) { - - sigma = 0; - - } - - _sigmas.push( sigma ); - - var texelSize = 1.0 / ( sizeLod - 1 ); - var min = - texelSize / 2; - var max = 1 + texelSize / 2; - var uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; - - var cubeFaces = 6; - var vertices = 6; - var positionSize = 3; - var uvSize = 2; - var faceIndexSize = 1; - - var position = new Float32Array( positionSize * vertices * cubeFaces ); - var uv = new Float32Array( uvSize * vertices * cubeFaces ); - var faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); - - for ( var face = 0; face < cubeFaces; face ++ ) { - - var x = ( face % 3 ) * 2 / 3 - 1; - var y = face > 2 ? 0 : - 1; - var coordinates = [ - x, y, 0, - x + 2 / 3, y, 0, - x + 2 / 3, y + 1, 0, - x, y, 0, - x + 2 / 3, y + 1, 0, - x, y + 1, 0 - ]; - position.set( coordinates, positionSize * vertices * face ); - uv.set( uv1, uvSize * vertices * face ); - var fill = [ face, face, face, face, face, face ]; - faceIndex.set( fill, faceIndexSize * vertices * face ); - - } - - var planes = new BufferGeometry(); - planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) ); - planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) ); - planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) ); - _lodPlanes.push( planes ); - - if ( lod > LOD_MIN ) { - - lod --; - - } - - } - - return { _lodPlanes: _lodPlanes, _sizeLods: _sizeLods, _sigmas: _sigmas }; - - } - - function _createRenderTarget( params ) { - - var cubeUVRenderTarget = new WebGLRenderTarget( 3 * SIZE_MAX, 3 * SIZE_MAX, params ); - cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; - cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; - cubeUVRenderTarget.scissorTest = true; - return cubeUVRenderTarget; - - } - - function _setViewport( target, x, y, width, height ) { - - target.viewport.set( x, y, width, height ); - target.scissor.set( x, y, width, height ); - - } - - function _getBlurShader( maxSamples ) { - - var weights = new Float32Array( maxSamples ); - var poleAxis = new Vector3( 0, 1, 0 ); - var shaderMaterial = new RawShaderMaterial( { - - defines: { 'n': maxSamples }, - - uniforms: { - 'envMap': { value: null }, - 'samples': { value: 1 }, - 'weights': { value: weights }, - 'latitudinal': { value: false }, - 'dTheta': { value: 0 }, - 'mipInt': { value: 0 }, - 'poleAxis': { value: poleAxis }, - 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] }, - 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] } - }, - - vertexShader: _getCommonVertexShader(), - - fragmentShader: ("\nprecision mediump float;\nprecision mediump int;\nvarying vec3 vOutputDirection;\nuniform sampler2D envMap;\nuniform int samples;\nuniform float weights[n];\nuniform bool latitudinal;\nuniform float dTheta;\nuniform float mipInt;\nuniform vec3 poleAxis;\n\n" + (_getEncodings()) + "\n\n#define ENVMAP_TYPE_CUBE_UV\n#include \n\nvec3 getSample(float theta, vec3 axis) {\n\tfloat cosTheta = cos(theta);\n\t// Rodrigues' axis-angle rotation\n\tvec3 sampleDirection = vOutputDirection * cosTheta\n\t\t+ cross(axis, vOutputDirection) * sin(theta)\n\t\t+ axis * dot(axis, vOutputDirection) * (1.0 - cosTheta);\n\treturn bilinearCubeUV(envMap, sampleDirection, mipInt);\n}\n\nvoid main() {\n\tvec3 axis = latitudinal ? poleAxis : cross(poleAxis, vOutputDirection);\n\tif (all(equal(axis, vec3(0.0))))\n\t\taxis = vec3(vOutputDirection.z, 0.0, - vOutputDirection.x);\n\taxis = normalize(axis);\n\tgl_FragColor = vec4(0.0);\n\tgl_FragColor.rgb += weights[0] * getSample(0.0, axis);\n\tfor (int i = 1; i < n; i++) {\n\t\tif (i >= samples)\n\t\t\tbreak;\n\t\tfloat theta = dTheta * float(i);\n\t\tgl_FragColor.rgb += weights[i] * getSample(-1.0 * theta, axis);\n\t\tgl_FragColor.rgb += weights[i] * getSample(theta, axis);\n\t}\n\tgl_FragColor = linearToOutputTexel(gl_FragColor);\n}\n\t\t"), - - blending: NoBlending, - depthTest: false, - depthWrite: false - - } ); - - shaderMaterial.type = 'SphericalGaussianBlur'; - - return shaderMaterial; - - } - - function _getEquirectShader() { - - var texelSize = new Vector2( 1, 1 ); - var shaderMaterial = new RawShaderMaterial( { - - uniforms: { - 'envMap': { value: null }, - 'texelSize': { value: texelSize }, - 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] }, - 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] } - }, - - vertexShader: _getCommonVertexShader(), - - fragmentShader: ("\nprecision mediump float;\nprecision mediump int;\nvarying vec3 vOutputDirection;\nuniform sampler2D envMap;\nuniform vec2 texelSize;\n\n" + (_getEncodings()) + "\n\n#include \n\nvoid main() {\n\tgl_FragColor = vec4(0.0);\n\tvec3 outputDirection = normalize(vOutputDirection);\n\tvec2 uv = equirectUv( outputDirection );\n\tvec2 f = fract(uv / texelSize - 0.5);\n\tuv -= f * texelSize;\n\tvec3 tl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;\n\tuv.x += texelSize.x;\n\tvec3 tr = envMapTexelToLinear(texture2D(envMap, uv)).rgb;\n\tuv.y += texelSize.y;\n\tvec3 br = envMapTexelToLinear(texture2D(envMap, uv)).rgb;\n\tuv.x -= texelSize.x;\n\tvec3 bl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;\n\tvec3 tm = mix(tl, tr, f.x);\n\tvec3 bm = mix(bl, br, f.x);\n\tgl_FragColor.rgb = mix(tm, bm, f.y);\n\tgl_FragColor = linearToOutputTexel(gl_FragColor);\n}\n\t\t"), - - blending: NoBlending, - depthTest: false, - depthWrite: false - - } ); - - shaderMaterial.type = 'EquirectangularToCubeUV'; - - return shaderMaterial; - - } - - function _getCubemapShader() { - - var shaderMaterial = new RawShaderMaterial( { - - uniforms: { - 'envMap': { value: null }, - 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] }, - 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] } - }, - - vertexShader: _getCommonVertexShader(), - - fragmentShader: ("\nprecision mediump float;\nprecision mediump int;\nvarying vec3 vOutputDirection;\nuniform samplerCube envMap;\n\n" + (_getEncodings()) + "\n\nvoid main() {\n\tgl_FragColor = vec4(0.0);\n\tgl_FragColor.rgb = envMapTexelToLinear(textureCube(envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ))).rgb;\n\tgl_FragColor = linearToOutputTexel(gl_FragColor);\n}\n\t\t"), - - blending: NoBlending, - depthTest: false, - depthWrite: false - - } ); - - shaderMaterial.type = 'CubemapToCubeUV'; - - return shaderMaterial; - - } - - function _getCommonVertexShader() { - - return "\nprecision mediump float;\nprecision mediump int;\nattribute vec3 position;\nattribute vec2 uv;\nattribute float faceIndex;\nvarying vec3 vOutputDirection;\n\n// RH coordinate system; PMREM face-indexing convention\nvec3 getDirection(vec2 uv, float face) {\n\tuv = 2.0 * uv - 1.0;\n\tvec3 direction = vec3(uv, 1.0);\n\tif (face == 0.0) {\n\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\t} else if (face == 1.0) {\n\t\tdirection = direction.xzy;\n\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\t} else if (face == 2.0) {\n\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\t} else if (face == 3.0) {\n\t\tdirection = direction.zyx;\n\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\t} else if (face == 4.0) {\n\t\tdirection = direction.xzy;\n\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\t} else if (face == 5.0) {\n\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\t}\n\treturn direction;\n}\n\nvoid main() {\n\tvOutputDirection = getDirection(uv, faceIndex);\n\tgl_Position = vec4( position, 1.0 );\n}\n\t"; - - } - - function _getEncodings() { - - return "\nuniform int inputEncoding;\nuniform int outputEncoding;\n\n#include \n\nvec4 inputTexelToLinear(vec4 value){\n\tif(inputEncoding == 0){\n\t\treturn value;\n\t}else if(inputEncoding == 1){\n\t\treturn sRGBToLinear(value);\n\t}else if(inputEncoding == 2){\n\t\treturn RGBEToLinear(value);\n\t}else if(inputEncoding == 3){\n\t\treturn RGBMToLinear(value, 7.0);\n\t}else if(inputEncoding == 4){\n\t\treturn RGBMToLinear(value, 16.0);\n\t}else if(inputEncoding == 5){\n\t\treturn RGBDToLinear(value, 256.0);\n\t}else{\n\t\treturn GammaToLinear(value, 2.2);\n\t}\n}\n\nvec4 linearToOutputTexel(vec4 value){\n\tif(outputEncoding == 0){\n\t\treturn value;\n\t}else if(outputEncoding == 1){\n\t\treturn LinearTosRGB(value);\n\t}else if(outputEncoding == 2){\n\t\treturn LinearToRGBE(value);\n\t}else if(outputEncoding == 3){\n\t\treturn LinearToRGBM(value, 7.0);\n\t}else if(outputEncoding == 4){\n\t\treturn LinearToRGBM(value, 16.0);\n\t}else if(outputEncoding == 5){\n\t\treturn LinearToRGBD(value, 256.0);\n\t}else{\n\t\treturn LinearToGamma(value, 2.2);\n\t}\n}\n\nvec4 envMapTexelToLinear(vec4 color) {\n\treturn inputTexelToLinear(color);\n}\n\t"; - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Face4( a, b, c, d, normal, color, materialIndex ) { - - console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ); - return new Face3( a, b, c, normal, color, materialIndex ); - - } - - var LineStrip = 0; - var LinePieces = 1; - var NoColors = 0; - var FaceColors = 1; - var VertexColors = 2; - - function MeshFaceMaterial( materials ) { - - console.warn( 'THREE.MeshFaceMaterial has been removed. Use an Array instead.' ); - return materials; - - } - - function MultiMaterial( materials ) { - - if ( materials === undefined ) { materials = []; } - - console.warn( 'THREE.MultiMaterial has been removed. Use an Array instead.' ); - materials.isMultiMaterial = true; - materials.materials = materials; - materials.clone = function () { - - return materials.slice(); - - }; - - return materials; - - } - - function PointCloud( geometry, material ) { - - console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); - return new Points( geometry, material ); - - } - - function Particle( material ) { - - console.warn( 'THREE.Particle has been renamed to THREE.Sprite.' ); - return new Sprite( material ); - - } - - function ParticleSystem( geometry, material ) { - - console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); - return new Points( geometry, material ); - - } - - function PointCloudMaterial( parameters ) { - - console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); - return new PointsMaterial( parameters ); - - } - - function ParticleBasicMaterial( parameters ) { - - console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); - return new PointsMaterial( parameters ); - - } - - function ParticleSystemMaterial( parameters ) { - - console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); - return new PointsMaterial( parameters ); - - } - - function Vertex( x, y, z ) { - - console.warn( 'THREE.Vertex has been removed. Use THREE.Vector3 instead.' ); - return new Vector3( x, y, z ); - - } - - // - - function DynamicBufferAttribute( array, itemSize ) { - - console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead.' ); - return new BufferAttribute( array, itemSize ).setUsage( DynamicDrawUsage ); - - } - - function Int8Attribute( array, itemSize ) { - - console.warn( 'THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead.' ); - return new Int8BufferAttribute( array, itemSize ); - - } - - function Uint8Attribute( array, itemSize ) { - - console.warn( 'THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead.' ); - return new Uint8BufferAttribute( array, itemSize ); - - } - - function Uint8ClampedAttribute( array, itemSize ) { - - console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead.' ); - return new Uint8ClampedBufferAttribute( array, itemSize ); - - } - - function Int16Attribute( array, itemSize ) { - - console.warn( 'THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead.' ); - return new Int16BufferAttribute( array, itemSize ); - - } - - function Uint16Attribute( array, itemSize ) { - - console.warn( 'THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead.' ); - return new Uint16BufferAttribute( array, itemSize ); - - } - - function Int32Attribute( array, itemSize ) { - - console.warn( 'THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead.' ); - return new Int32BufferAttribute( array, itemSize ); - - } - - function Uint32Attribute( array, itemSize ) { - - console.warn( 'THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead.' ); - return new Uint32BufferAttribute( array, itemSize ); - - } - - function Float32Attribute( array, itemSize ) { - - console.warn( 'THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead.' ); - return new Float32BufferAttribute( array, itemSize ); - - } - - function Float64Attribute( array, itemSize ) { - - console.warn( 'THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead.' ); - return new Float64BufferAttribute( array, itemSize ); - - } - - // - - Curve.create = function ( construct, getPoint ) { - - console.log( 'THREE.Curve.create() has been deprecated' ); - - construct.prototype = Object.create( Curve.prototype ); - construct.prototype.constructor = construct; - construct.prototype.getPoint = getPoint; - - return construct; - - }; - - // - - Object.assign( CurvePath.prototype, { - - createPointsGeometry: function ( divisions ) { - - console.warn( 'THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); - - // generate geometry from path points (for Line or Points objects) - - var pts = this.getPoints( divisions ); - return this.createGeometry( pts ); - - }, - - createSpacedPointsGeometry: function ( divisions ) { - - console.warn( 'THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); - - // generate geometry from equidistant sampling along the path - - var pts = this.getSpacedPoints( divisions ); - return this.createGeometry( pts ); - - }, - - createGeometry: function ( points ) { - - console.warn( 'THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); - - var geometry = new Geometry(); - - for ( var i = 0, l = points.length; i < l; i ++ ) { - - var point = points[ i ]; - geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); - - } - - return geometry; - - } - - } ); - - // - - Object.assign( Path.prototype, { - - fromPoints: function ( points ) { - - console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); - return this.setFromPoints( points ); - - } - - } ); - - // - - function ClosedSplineCurve3( points ) { - - console.warn( 'THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' ); - - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; - this.closed = true; - - } - - ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); - - // - - function SplineCurve3( points ) { - - console.warn( 'THREE.SplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' ); - - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; - - } - - SplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); - - // - - function Spline( points ) { - - console.warn( 'THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead.' ); - - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; - - } - - Spline.prototype = Object.create( CatmullRomCurve3.prototype ); - - Object.assign( Spline.prototype, { - - initFromArray: function ( /* a */ ) { - - console.error( 'THREE.Spline: .initFromArray() has been removed.' ); - - }, - getControlPointsArray: function ( /* optionalTarget */ ) { - - console.error( 'THREE.Spline: .getControlPointsArray() has been removed.' ); - - }, - reparametrizeByArcLength: function ( /* samplingCoef */ ) { - - console.error( 'THREE.Spline: .reparametrizeByArcLength() has been removed.' ); - - } - - } ); - - // - - function AxisHelper( size ) { - - console.warn( 'THREE.AxisHelper has been renamed to THREE.AxesHelper.' ); - return new AxesHelper( size ); - - } - - function BoundingBoxHelper( object, color ) { - - console.warn( 'THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead.' ); - return new BoxHelper( object, color ); - - } - - function EdgesHelper( object, hex ) { - - console.warn( 'THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead.' ); - return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); - - } - - GridHelper.prototype.setColors = function () { - - console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); - - }; - - SkeletonHelper.prototype.update = function () { - - console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' ); - - }; - - function WireframeHelper( object, hex ) { - - console.warn( 'THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead.' ); - return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); - - } - - // - - Object.assign( Loader.prototype, { - - extractUrlBase: function ( url ) { - - console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); - return LoaderUtils.extractUrlBase( url ); - - } - - } ); - - Loader.Handlers = { - - add: function ( /* regex, loader */ ) { - - console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); - - }, - - get: function ( /* file */ ) { - - console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); - - } - - }; - - function XHRLoader( manager ) { - - console.warn( 'THREE.XHRLoader has been renamed to THREE.FileLoader.' ); - return new FileLoader( manager ); - - } - - function BinaryTextureLoader( manager ) { - - console.warn( 'THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader.' ); - return new DataTextureLoader( manager ); - - } - - Object.assign( ObjectLoader.prototype, { - - setTexturePath: function ( value ) { - - console.warn( 'THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath().' ); - return this.setResourcePath( value ); - - } - - } ); - - // - - Object.assign( Box2.prototype, { - - center: function ( optionalTarget ) { - - console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }, - empty: function () { - - console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - size: function ( optionalTarget ) { - - console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); - - } - } ); - - Object.assign( Box3.prototype, { - - center: function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }, - empty: function () { - - console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - isIntersectionSphere: function ( sphere ) { - - console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - }, - size: function ( optionalTarget ) { - - console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); - - } - } ); - - Object.assign( Sphere.prototype, { - - empty: function () { - - console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); - - }, - - } ); - - Frustum.prototype.setFromMatrix = function ( m ) { - - console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); - return this.setFromProjectionMatrix( m ); - - }; - - Line3.prototype.center = function ( optionalTarget ) { - - console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); - - }; - - Object.assign( MathUtils, { - - random16: function () { - - console.warn( 'THREE.Math: .random16() has been deprecated. Use Math.random() instead.' ); - return Math.random(); - - }, - - nearestPowerOfTwo: function ( value ) { - - console.warn( 'THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().' ); - return MathUtils.floorPowerOfTwo( value ); - - }, - - nextPowerOfTwo: function ( value ) { - - console.warn( 'THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().' ); - return MathUtils.ceilPowerOfTwo( value ); - - } - - } ); - - Object.assign( Matrix3.prototype, { - - flattenToArrayOffset: function ( array, offset ) { - - console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); - - }, - multiplyVector3: function ( vector ) { - - console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); - - }, - multiplyVector3Array: function ( /* a */ ) { - - console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); - - }, - applyToBufferAttribute: function ( attribute ) { - - console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); - return attribute.applyMatrix3( this ); - - }, - applyToVector3Array: function ( /* array, offset, length */ ) { - - console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); - - } - - } ); - - Object.assign( Matrix4.prototype, { - - extractPosition: function ( m ) { - - console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); - - }, - flattenToArrayOffset: function ( array, offset ) { - - console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); - - }, - getPosition: function () { - - console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); - return new Vector3().setFromMatrixColumn( this, 3 ); - - }, - setRotationFromQuaternion: function ( q ) { - - console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - return this.makeRotationFromQuaternion( q ); - - }, - multiplyToArray: function () { - - console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); - - }, - multiplyVector3: function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - multiplyVector4: function ( vector ) { - - console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - multiplyVector3Array: function ( /* a */ ) { - - console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); - - }, - rotateAxis: function ( v ) { - - console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); - v.transformDirection( this ); - - }, - crossVector: function ( vector ) { - - console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); - - }, - translate: function () { - - console.error( 'THREE.Matrix4: .translate() has been removed.' ); - - }, - rotateX: function () { - - console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); - - }, - rotateY: function () { - - console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); - - }, - rotateZ: function () { - - console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); - - }, - rotateByAxis: function () { - - console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); - - }, - applyToBufferAttribute: function ( attribute ) { - - console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); - return attribute.applyMatrix4( this ); - - }, - applyToVector3Array: function ( /* array, offset, length */ ) { - - console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); - - }, - makeFrustum: function ( left, right, bottom, top, near, far ) { - - console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); - return this.makePerspective( left, right, top, bottom, near, far ); - - } - - } ); - - Plane.prototype.isIntersectionLine = function ( line ) { - - console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); - return this.intersectsLine( line ); - - }; - - Quaternion.prototype.multiplyVector3 = function ( vector ) { - - console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); - - }; - - Object.assign( Ray.prototype, { - - isIntersectionBox: function ( box ) { - - console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); - - }, - isIntersectionPlane: function ( plane ) { - - console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); - return this.intersectsPlane( plane ); - - }, - isIntersectionSphere: function ( sphere ) { - - console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - - } - - } ); - - Object.assign( Triangle.prototype, { - - area: function () { - - console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); - return this.getArea(); - - }, - barycoordFromPoint: function ( point, target ) { - - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return this.getBarycoord( point, target ); - - }, - midpoint: function ( target ) { - - console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); - return this.getMidpoint( target ); - - }, - normal: function ( target ) { - - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return this.getNormal( target ); - - }, - plane: function ( target ) { - - console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); - return this.getPlane( target ); - - } - - } ); - - Object.assign( Triangle, { - - barycoordFromPoint: function ( point, a, b, c, target ) { - - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return Triangle.getBarycoord( point, a, b, c, target ); - - }, - normal: function ( a, b, c, target ) { - - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return Triangle.getNormal( a, b, c, target ); - - } - - } ); - - Object.assign( Shape.prototype, { - - extractAllPoints: function ( divisions ) { - - console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); - return this.extractPoints( divisions ); - - }, - extrude: function ( options ) { - - console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); - return new ExtrudeGeometry( this, options ); - - }, - makeGeometry: function ( options ) { - - console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); - return new ShapeGeometry( this, options ); - - } - - } ); - - Object.assign( Vector2.prototype, { - - fromAttribute: function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }, - distanceToManhattan: function ( v ) { - - console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); - - }, - lengthManhattan: function () { - - console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - } - - } ); - - Object.assign( Vector3.prototype, { - - setEulerFromRotationMatrix: function () { - - console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); - - }, - setEulerFromQuaternion: function () { - - console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); - - }, - getPositionFromMatrix: function ( m ) { - - console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - return this.setFromMatrixPosition( m ); - - }, - getScaleFromMatrix: function ( m ) { - - console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - return this.setFromMatrixScale( m ); - - }, - getColumnFromMatrix: function ( index, matrix ) { - - console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - return this.setFromMatrixColumn( matrix, index ); - - }, - applyProjection: function ( m ) { - - console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); - return this.applyMatrix4( m ); - - }, - fromAttribute: function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }, - distanceToManhattan: function ( v ) { - - console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); - - }, - lengthManhattan: function () { - - console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - } - - } ); - - Object.assign( Vector4.prototype, { - - fromAttribute: function ( attribute, index, offset ) { - - console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); - - }, - lengthManhattan: function () { - - console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - - } - - } ); - - // - - Object.assign( Geometry.prototype, { - - computeTangents: function () { - - console.error( 'THREE.Geometry: .computeTangents() has been removed.' ); - - }, - computeLineDistances: function () { - - console.error( 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.' ); - - }, - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.Geometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - } - - } ); - - Object.assign( Object3D.prototype, { - - getChildByName: function ( name ) { - - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); - - }, - renderDepth: function () { - - console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); - - }, - translate: function ( distance, axis ) { - - console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); - - }, - getWorldRotation: function () { - - console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); - - }, - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - } - - } ); - - Object.defineProperties( Object3D.prototype, { - - eulerOrder: { - get: function () { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - return this.rotation.order; - - }, - set: function ( value ) { - - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - this.rotation.order = value; - - } - }, - useQuaternion: { - get: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - }, - set: function () { - - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - - } - } - - } ); - - Object.assign( Mesh.prototype, { - - setDrawMode: function () { - - console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - - }, - - } ); - - Object.defineProperties( Mesh.prototype, { - - drawMode: { - get: function () { - - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.' ); - return TrianglesDrawMode; - - }, - set: function () { - - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - - } - } - - } ); - - Object.defineProperties( LOD.prototype, { - - objects: { - get: function () { - - console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); - return this.levels; - - } - } - - } ); - - Object.defineProperty( Skeleton.prototype, 'useVertexTexture', { - - get: function () { - - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); - - } - - } ); - - SkinnedMesh.prototype.initBones = function () { - - console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); - - }; - - Object.defineProperty( Curve.prototype, '__arcLengthDivisions', { - - get: function () { - - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - return this.arcLengthDivisions; - - }, - set: function ( value ) { - - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - this.arcLengthDivisions = value; - - } - - } ); - - // - - PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { - - console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + - "Use .setFocalLength and .filmGauge for a photographic setup." ); - - if ( filmGauge !== undefined ) { this.filmGauge = filmGauge; } - this.setFocalLength( focalLength ); - - }; - - // - - Object.defineProperties( Light.prototype, { - onlyShadow: { - set: function () { - - console.warn( 'THREE.Light: .onlyShadow has been removed.' ); - - } - }, - shadowCameraFov: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); - this.shadow.camera.fov = value; - - } - }, - shadowCameraLeft: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); - this.shadow.camera.left = value; - - } - }, - shadowCameraRight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); - this.shadow.camera.right = value; - - } - }, - shadowCameraTop: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); - this.shadow.camera.top = value; - - } - }, - shadowCameraBottom: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); - this.shadow.camera.bottom = value; - - } - }, - shadowCameraNear: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); - this.shadow.camera.near = value; - - } - }, - shadowCameraFar: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); - this.shadow.camera.far = value; - - } - }, - shadowCameraVisible: { - set: function () { - - console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); - - } - }, - shadowBias: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); - this.shadow.bias = value; - - } - }, - shadowDarkness: { - set: function () { - - console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); - - } - }, - shadowMapWidth: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); - this.shadow.mapSize.width = value; - - } - }, - shadowMapHeight: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); - this.shadow.mapSize.height = value; - - } - } - } ); - - // - - Object.defineProperties( BufferAttribute.prototype, { - - length: { - get: function () { - - console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); - return this.array.length; - - } - }, - dynamic: { - get: function () { - - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; - - }, - set: function ( /* value */ ) { - - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - this.setUsage( DynamicDrawUsage ); - - } - } - - } ); - - Object.assign( BufferAttribute.prototype, { - setDynamic: function ( value ) { - - console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; - - }, - copyIndicesArray: function ( /* indices */ ) { - - console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); - - }, - setArray: function ( /* array */ ) { - - console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - - } - } ); - - Object.assign( BufferGeometry.prototype, { - - addIndex: function ( index ) { - - console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); - this.setIndex( index ); - - }, - addAttribute: function ( name, attribute ) { - - console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); - - if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { - - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - - return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - - } - - if ( name === 'index' ) { - - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); - - return this; - - } - - return this.setAttribute( name, attribute ); - - }, - addDrawCall: function ( start, count, indexOffset ) { - - if ( indexOffset !== undefined ) { - - console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); - - } - - console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); - this.addGroup( start, count ); - - }, - clearDrawCalls: function () { - - console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); - this.clearGroups(); - - }, - computeTangents: function () { - - console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); - - }, - computeOffsets: function () { - - console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); - - }, - removeAttribute: function ( name ) { - - console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); - - return this.deleteAttribute( name ); - - }, - applyMatrix: function ( matrix ) { - - console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); - - } - - } ); - - Object.defineProperties( BufferGeometry.prototype, { - - drawcalls: { - get: function () { - - console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); - return this.groups; - - } - }, - offsets: { - get: function () { - - console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); - return this.groups; - - } - } - - } ); - - Object.defineProperties( InstancedBufferGeometry.prototype, { - - maxInstancedCount: { - get: function () { - - console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' ); - return this.instanceCount; - - }, - set: function ( value ) { - - console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' ); - this.instanceCount = value; - - } - } - - } ); - - Object.defineProperties( Raycaster.prototype, { - - linePrecision: { - get: function () { - - console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' ); - return this.params.Line.threshold; - - }, - set: function ( value ) { - - console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' ); - this.params.Line.threshold = value; - - } - } - - } ); - - Object.defineProperties( InterleavedBuffer.prototype, { - - dynamic: { - get: function () { - - console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; - - }, - set: function ( value ) { - - console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' ); - this.setUsage( value ); - - } - } - - } ); - - Object.assign( InterleavedBuffer.prototype, { - setDynamic: function ( value ) { - - console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; - - }, - setArray: function ( /* array */ ) { - - console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - - } - } ); - - // - - Object.assign( ExtrudeBufferGeometry.prototype, { - - getArrays: function () { - - console.error( 'THREE.ExtrudeBufferGeometry: .getArrays() has been removed.' ); - - }, - - addShapeList: function () { - - console.error( 'THREE.ExtrudeBufferGeometry: .addShapeList() has been removed.' ); - - }, - - addShape: function () { - - console.error( 'THREE.ExtrudeBufferGeometry: .addShape() has been removed.' ); - - } - - } ); - - // - - Object.defineProperties( Uniform.prototype, { - - dynamic: { - set: function () { - - console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' ); - - } - }, - onUpdate: { - value: function () { - - console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' ); - return this; - - } - } - - } ); - - // - - Object.defineProperties( Material.prototype, { - - wrapAround: { - get: function () { - - console.warn( 'THREE.Material: .wrapAround has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Material: .wrapAround has been removed.' ); - - } - }, - - overdraw: { - get: function () { - - console.warn( 'THREE.Material: .overdraw has been removed.' ); - - }, - set: function () { - - console.warn( 'THREE.Material: .overdraw has been removed.' ); - - } - }, - - wrapRGB: { - get: function () { - - console.warn( 'THREE.Material: .wrapRGB has been removed.' ); - return new Color(); - - } - }, - - shading: { - get: function () { - - console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - - }, - set: function ( value ) { - - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( value === FlatShading ); - - } - }, - - stencilMask: { - get: function () { - - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - return this.stencilFuncMask; - - }, - set: function ( value ) { - - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - this.stencilFuncMask = value; - - } - } - - } ); - - Object.defineProperties( MeshPhongMaterial.prototype, { - - metal: { - get: function () { - - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' ); - return false; - - }, - set: function () { - - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' ); - - } - } - - } ); - - Object.defineProperties( ShaderMaterial.prototype, { - - derivatives: { - get: function () { - - console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - return this.extensions.derivatives; - - }, - set: function ( value ) { - - console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - this.extensions.derivatives = value; - - } - } - - } ); - - // - - Object.assign( WebGLRenderer.prototype, { - - clearTarget: function ( renderTarget, color, depth, stencil ) { - - console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); - - }, - animate: function ( callback ) { - - console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); - this.setAnimationLoop( callback ); - - }, - getCurrentRenderTarget: function () { - - console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); - return this.getRenderTarget(); - - }, - getMaxAnisotropy: function () { - - console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); - return this.capabilities.getMaxAnisotropy(); - - }, - getPrecision: function () { - - console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); - return this.capabilities.precision; - - }, - resetGLState: function () { - - console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); - return this.state.reset(); - - }, - supportsFloatTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return this.extensions.get( 'OES_texture_float' ); - - }, - supportsHalfFloatTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return this.extensions.get( 'OES_texture_half_float' ); - - }, - supportsStandardDerivatives: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return this.extensions.get( 'OES_standard_derivatives' ); - - }, - supportsCompressedTextureS3TC: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); - - }, - supportsCompressedTexturePVRTC: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - - }, - supportsBlendMinMax: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return this.extensions.get( 'EXT_blend_minmax' ); - - }, - supportsVertexTextures: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); - return this.capabilities.vertexTextures; - - }, - supportsInstancedArrays: function () { - - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return this.extensions.get( 'ANGLE_instanced_arrays' ); - - }, - enableScissorTest: function ( boolean ) { - - console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); - this.setScissorTest( boolean ); - - }, - initMaterial: function () { - - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); - - }, - addPrePlugin: function () { - - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - - }, - addPostPlugin: function () { - - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - - }, - updateShadowMap: function () { - - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - - }, - setFaceCulling: function () { - - console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); - - }, - allocTextureUnit: function () { - - console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); - - }, - setTexture: function () { - - console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); - - }, - setTexture2D: function () { - - console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); - - }, - setTextureCube: function () { - - console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); - - }, - getActiveMipMapLevel: function () { - - console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); - return this.getActiveMipmapLevel(); - - } - - } ); - - Object.defineProperties( WebGLRenderer.prototype, { - - shadowMapEnabled: { - get: function () { - - return this.shadowMap.enabled; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); - this.shadowMap.enabled = value; - - } - }, - shadowMapType: { - get: function () { - - return this.shadowMap.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); - this.shadowMap.type = value; - - } - }, - shadowMapCullFace: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function ( /* value */ ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - - } - }, - context: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' ); - return this.getContext(); - - } - }, - vr: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .vr has been renamed to .xr' ); - return this.xr; - - } - }, - gammaInput: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - return false; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - - } - }, - gammaOutput: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - return false; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - this.outputEncoding = ( value === true ) ? sRGBEncoding : LinearEncoding; - - } - } - - } ); - - Object.defineProperties( WebGLShadowMap.prototype, { - - cullFace: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function ( /* cullFace */ ) { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - - } - }, - renderReverseSided: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - - } - }, - renderSingleSided: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - - } - } - - } ); - - function WebGLRenderTargetCube( width, height, options ) { - - console.warn( 'THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options ).' ); - return new WebGLCubeRenderTarget( width, options ); - - } - - // - - Object.defineProperties( WebGLRenderTarget.prototype, { - - wrapS: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - return this.texture.wrapS; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - this.texture.wrapS = value; - - } - }, - wrapT: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - return this.texture.wrapT; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - this.texture.wrapT = value; - - } - }, - magFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - return this.texture.magFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - this.texture.magFilter = value; - - } - }, - minFilter: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - return this.texture.minFilter; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - this.texture.minFilter = value; - - } - }, - anisotropy: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - return this.texture.anisotropy; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - this.texture.anisotropy = value; - - } - }, - offset: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - return this.texture.offset; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - this.texture.offset = value; - - } - }, - repeat: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - return this.texture.repeat; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - this.texture.repeat = value; - - } - }, - format: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - return this.texture.format; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - this.texture.format = value; - - } - }, - type: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - return this.texture.type; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - this.texture.type = value; - - } - }, - generateMipmaps: { - get: function () { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - return this.texture.generateMipmaps; - - }, - set: function ( value ) { - - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - this.texture.generateMipmaps = value; - - } - } - - } ); - - // - - Object.defineProperties( Audio.prototype, { - - load: { - value: function ( file ) { - - console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); - var scope = this; - var audioLoader = new AudioLoader(); - audioLoader.load( file, function ( buffer ) { - - scope.setBuffer( buffer ); - - } ); - return this; - - } - }, - startTime: { - set: function () { - - console.warn( 'THREE.Audio: .startTime is now .play( delay ).' ); - - } - } - - } ); - - AudioAnalyser.prototype.getData = function () { - - console.warn( 'THREE.AudioAnalyser: .getData() is now .getFrequencyData().' ); - return this.getFrequencyData(); - - }; - - // - - CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { - - console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' ); - return this.update( renderer, scene ); - - }; - - // - - var GeometryUtils = { - - merge: function ( geometry1, geometry2, materialIndexOffset ) { - - console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); - var matrix; - - if ( geometry2.isMesh ) { - - geometry2.matrixAutoUpdate && geometry2.updateMatrix(); - - matrix = geometry2.matrix; - geometry2 = geometry2.geometry; - - } - - geometry1.merge( geometry2, matrix, materialIndexOffset ); - - }, - - center: function ( geometry ) { - - console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); - return geometry.center(); - - } - - }; - - ImageUtils.crossOrigin = undefined; - - ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - - var loader = new TextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - var texture = loader.load( url, onLoad, undefined, onError ); - - if ( mapping ) { texture.mapping = mapping; } - - return texture; - - }; - - ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { - - console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); - - var loader = new CubeTextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); - - var texture = loader.load( urls, onLoad, undefined, onError ); - - if ( mapping ) { texture.mapping = mapping; } - - return texture; - - }; - - ImageUtils.loadCompressedTexture = function () { - - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); - - }; - - ImageUtils.loadCompressedTextureCube = function () { - - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); - - }; - - // - - function CanvasRenderer() { - - console.error( 'THREE.CanvasRenderer has been removed' ); - - } - - // - - function JSONLoader() { - - console.error( 'THREE.JSONLoader has been removed.' ); - - } - - // - - var SceneUtils = { - - createMultiMaterialObject: function ( /* geometry, materials */ ) { - - console.error( 'THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js' ); - - }, - - detach: function ( /* child, parent, scene */ ) { - - console.error( 'THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js' ); - - }, - - attach: function ( /* child, scene, parent */ ) { - - console.error( 'THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js' ); - - } - - }; - - // - - function LensFlare() { - - console.error( 'THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js' ); - - } - - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - - /* eslint-disable no-undef */ - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { - revision: REVISION, - } } ) ); - /* eslint-enable no-undef */ - - } - - exports.ACESFilmicToneMapping = ACESFilmicToneMapping; - exports.AddEquation = AddEquation; - exports.AddOperation = AddOperation; - exports.AdditiveAnimationBlendMode = AdditiveAnimationBlendMode; - exports.AdditiveBlending = AdditiveBlending; - exports.AlphaFormat = AlphaFormat; - exports.AlwaysDepth = AlwaysDepth; - exports.AlwaysStencilFunc = AlwaysStencilFunc; - exports.AmbientLight = AmbientLight; - exports.AmbientLightProbe = AmbientLightProbe; - exports.AnimationClip = AnimationClip; - exports.AnimationLoader = AnimationLoader; - exports.AnimationMixer = AnimationMixer; - exports.AnimationObjectGroup = AnimationObjectGroup; - exports.AnimationUtils = AnimationUtils; - exports.ArcCurve = ArcCurve; - exports.ArrayCamera = ArrayCamera; - exports.ArrowHelper = ArrowHelper; - exports.Audio = Audio; - exports.AudioAnalyser = AudioAnalyser; - exports.AudioContext = AudioContext; - exports.AudioListener = AudioListener; - exports.AudioLoader = AudioLoader; - exports.AxesHelper = AxesHelper; - exports.AxisHelper = AxisHelper; - exports.BackSide = BackSide; - exports.BasicDepthPacking = BasicDepthPacking; - exports.BasicShadowMap = BasicShadowMap; - exports.BinaryTextureLoader = BinaryTextureLoader; - exports.Bone = Bone; - exports.BooleanKeyframeTrack = BooleanKeyframeTrack; - exports.BoundingBoxHelper = BoundingBoxHelper; - exports.Box2 = Box2; - exports.Box3 = Box3; - exports.Box3Helper = Box3Helper; - exports.BoxBufferGeometry = BoxBufferGeometry; - exports.BoxGeometry = BoxGeometry; - exports.BoxHelper = BoxHelper; - exports.BufferAttribute = BufferAttribute; - exports.BufferGeometry = BufferGeometry; - exports.BufferGeometryLoader = BufferGeometryLoader; - exports.ByteType = ByteType; - exports.Cache = Cache; - exports.Camera = Camera; - exports.CameraHelper = CameraHelper; - exports.CanvasRenderer = CanvasRenderer; - exports.CanvasTexture = CanvasTexture; - exports.CatmullRomCurve3 = CatmullRomCurve3; - exports.CineonToneMapping = CineonToneMapping; - exports.CircleBufferGeometry = CircleBufferGeometry; - exports.CircleGeometry = CircleGeometry; - exports.ClampToEdgeWrapping = ClampToEdgeWrapping; - exports.Clock = Clock; - exports.ClosedSplineCurve3 = ClosedSplineCurve3; - exports.Color = Color; - exports.ColorKeyframeTrack = ColorKeyframeTrack; - exports.CompressedTexture = CompressedTexture; - exports.CompressedTextureLoader = CompressedTextureLoader; - exports.ConeBufferGeometry = ConeBufferGeometry; - exports.ConeGeometry = ConeGeometry; - exports.CubeCamera = CubeCamera; - exports.CubeGeometry = BoxGeometry; - exports.CubeReflectionMapping = CubeReflectionMapping; - exports.CubeRefractionMapping = CubeRefractionMapping; - exports.CubeTexture = CubeTexture; - exports.CubeTextureLoader = CubeTextureLoader; - exports.CubeUVReflectionMapping = CubeUVReflectionMapping; - exports.CubeUVRefractionMapping = CubeUVRefractionMapping; - exports.CubicBezierCurve = CubicBezierCurve; - exports.CubicBezierCurve3 = CubicBezierCurve3; - exports.CubicInterpolant = CubicInterpolant; - exports.CullFaceBack = CullFaceBack; - exports.CullFaceFront = CullFaceFront; - exports.CullFaceFrontBack = CullFaceFrontBack; - exports.CullFaceNone = CullFaceNone; - exports.Curve = Curve; - exports.CurvePath = CurvePath; - exports.CustomBlending = CustomBlending; - exports.CylinderBufferGeometry = CylinderBufferGeometry; - exports.CylinderGeometry = CylinderGeometry; - exports.Cylindrical = Cylindrical; - exports.DataTexture = DataTexture; - exports.DataTexture2DArray = DataTexture2DArray; - exports.DataTexture3D = DataTexture3D; - exports.DataTextureLoader = DataTextureLoader; - exports.DecrementStencilOp = DecrementStencilOp; - exports.DecrementWrapStencilOp = DecrementWrapStencilOp; - exports.DefaultLoadingManager = DefaultLoadingManager; - exports.DepthFormat = DepthFormat; - exports.DepthStencilFormat = DepthStencilFormat; - exports.DepthTexture = DepthTexture; - exports.DirectionalLight = DirectionalLight; - exports.DirectionalLightHelper = DirectionalLightHelper; - exports.DirectionalLightShadow = DirectionalLightShadow; - exports.DiscreteInterpolant = DiscreteInterpolant; - exports.DodecahedronBufferGeometry = DodecahedronBufferGeometry; - exports.DodecahedronGeometry = DodecahedronGeometry; - exports.DoubleSide = DoubleSide; - exports.DstAlphaFactor = DstAlphaFactor; - exports.DstColorFactor = DstColorFactor; - exports.DynamicBufferAttribute = DynamicBufferAttribute; - exports.DynamicCopyUsage = DynamicCopyUsage; - exports.DynamicDrawUsage = DynamicDrawUsage; - exports.DynamicReadUsage = DynamicReadUsage; - exports.EdgesGeometry = EdgesGeometry; - exports.EdgesHelper = EdgesHelper; - exports.EllipseCurve = EllipseCurve; - exports.EqualDepth = EqualDepth; - exports.EqualStencilFunc = EqualStencilFunc; - exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; - exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; - exports.Euler = Euler; - exports.EventDispatcher = EventDispatcher; - exports.ExtrudeBufferGeometry = ExtrudeBufferGeometry; - exports.ExtrudeGeometry = ExtrudeGeometry; - exports.Face3 = Face3; - exports.Face4 = Face4; - exports.FaceColors = FaceColors; - exports.FileLoader = FileLoader; - exports.FlatShading = FlatShading; - exports.Float32Attribute = Float32Attribute; - exports.Float32BufferAttribute = Float32BufferAttribute; - exports.Float64Attribute = Float64Attribute; - exports.Float64BufferAttribute = Float64BufferAttribute; - exports.FloatType = FloatType; - exports.Fog = Fog; - exports.FogExp2 = FogExp2; - exports.Font = Font; - exports.FontLoader = FontLoader; - exports.FrontFaceDirectionCCW = FrontFaceDirectionCCW; - exports.FrontFaceDirectionCW = FrontFaceDirectionCW; - exports.FrontSide = FrontSide; - exports.Frustum = Frustum; - exports.GammaEncoding = GammaEncoding; - exports.Geometry = Geometry; - exports.GeometryUtils = GeometryUtils; - exports.GreaterDepth = GreaterDepth; - exports.GreaterEqualDepth = GreaterEqualDepth; - exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; - exports.GreaterStencilFunc = GreaterStencilFunc; - exports.GridHelper = GridHelper; - exports.Group = Group; - exports.HalfFloatType = HalfFloatType; - exports.HemisphereLight = HemisphereLight; - exports.HemisphereLightHelper = HemisphereLightHelper; - exports.HemisphereLightProbe = HemisphereLightProbe; - exports.IcosahedronBufferGeometry = IcosahedronBufferGeometry; - exports.IcosahedronGeometry = IcosahedronGeometry; - exports.ImageBitmapLoader = ImageBitmapLoader; - exports.ImageLoader = ImageLoader; - exports.ImageUtils = ImageUtils; - exports.ImmediateRenderObject = ImmediateRenderObject; - exports.IncrementStencilOp = IncrementStencilOp; - exports.IncrementWrapStencilOp = IncrementWrapStencilOp; - exports.InstancedBufferAttribute = InstancedBufferAttribute; - exports.InstancedBufferGeometry = InstancedBufferGeometry; - exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; - exports.InstancedMesh = InstancedMesh; - exports.Int16Attribute = Int16Attribute; - exports.Int16BufferAttribute = Int16BufferAttribute; - exports.Int32Attribute = Int32Attribute; - exports.Int32BufferAttribute = Int32BufferAttribute; - exports.Int8Attribute = Int8Attribute; - exports.Int8BufferAttribute = Int8BufferAttribute; - exports.IntType = IntType; - exports.InterleavedBuffer = InterleavedBuffer; - exports.InterleavedBufferAttribute = InterleavedBufferAttribute; - exports.Interpolant = Interpolant; - exports.InterpolateDiscrete = InterpolateDiscrete; - exports.InterpolateLinear = InterpolateLinear; - exports.InterpolateSmooth = InterpolateSmooth; - exports.InvertStencilOp = InvertStencilOp; - exports.JSONLoader = JSONLoader; - exports.KeepStencilOp = KeepStencilOp; - exports.KeyframeTrack = KeyframeTrack; - exports.LOD = LOD; - exports.LatheBufferGeometry = LatheBufferGeometry; - exports.LatheGeometry = LatheGeometry; - exports.Layers = Layers; - exports.LensFlare = LensFlare; - exports.LessDepth = LessDepth; - exports.LessEqualDepth = LessEqualDepth; - exports.LessEqualStencilFunc = LessEqualStencilFunc; - exports.LessStencilFunc = LessStencilFunc; - exports.Light = Light; - exports.LightProbe = LightProbe; - exports.LightShadow = LightShadow; - exports.Line = Line; - exports.Line3 = Line3; - exports.LineBasicMaterial = LineBasicMaterial; - exports.LineCurve = LineCurve; - exports.LineCurve3 = LineCurve3; - exports.LineDashedMaterial = LineDashedMaterial; - exports.LineLoop = LineLoop; - exports.LinePieces = LinePieces; - exports.LineSegments = LineSegments; - exports.LineStrip = LineStrip; - exports.LinearEncoding = LinearEncoding; - exports.LinearFilter = LinearFilter; - exports.LinearInterpolant = LinearInterpolant; - exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; - exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; - exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; - exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; - exports.LinearToneMapping = LinearToneMapping; - exports.Loader = Loader; - exports.LoaderUtils = LoaderUtils; - exports.LoadingManager = LoadingManager; - exports.LogLuvEncoding = LogLuvEncoding; - exports.LoopOnce = LoopOnce; - exports.LoopPingPong = LoopPingPong; - exports.LoopRepeat = LoopRepeat; - exports.LuminanceAlphaFormat = LuminanceAlphaFormat; - exports.LuminanceFormat = LuminanceFormat; - exports.MOUSE = MOUSE; - exports.Material = Material; - exports.MaterialLoader = MaterialLoader; - exports.Math = MathUtils; - exports.MathUtils = MathUtils; - exports.Matrix3 = Matrix3; - exports.Matrix4 = Matrix4; - exports.MaxEquation = MaxEquation; - exports.Mesh = Mesh; - exports.MeshBasicMaterial = MeshBasicMaterial; - exports.MeshDepthMaterial = MeshDepthMaterial; - exports.MeshDistanceMaterial = MeshDistanceMaterial; - exports.MeshFaceMaterial = MeshFaceMaterial; - exports.MeshLambertMaterial = MeshLambertMaterial; - exports.MeshMatcapMaterial = MeshMatcapMaterial; - exports.MeshNormalMaterial = MeshNormalMaterial; - exports.MeshPhongMaterial = MeshPhongMaterial; - exports.MeshPhysicalMaterial = MeshPhysicalMaterial; - exports.MeshStandardMaterial = MeshStandardMaterial; - exports.MeshToonMaterial = MeshToonMaterial; - exports.MinEquation = MinEquation; - exports.MirroredRepeatWrapping = MirroredRepeatWrapping; - exports.MixOperation = MixOperation; - exports.MultiMaterial = MultiMaterial; - exports.MultiplyBlending = MultiplyBlending; - exports.MultiplyOperation = MultiplyOperation; - exports.NearestFilter = NearestFilter; - exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; - exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; - exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; - exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; - exports.NeverDepth = NeverDepth; - exports.NeverStencilFunc = NeverStencilFunc; - exports.NoBlending = NoBlending; - exports.NoColors = NoColors; - exports.NoToneMapping = NoToneMapping; - exports.NormalAnimationBlendMode = NormalAnimationBlendMode; - exports.NormalBlending = NormalBlending; - exports.NotEqualDepth = NotEqualDepth; - exports.NotEqualStencilFunc = NotEqualStencilFunc; - exports.NumberKeyframeTrack = NumberKeyframeTrack; - exports.Object3D = Object3D; - exports.ObjectLoader = ObjectLoader; - exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; - exports.OctahedronBufferGeometry = OctahedronBufferGeometry; - exports.OctahedronGeometry = OctahedronGeometry; - exports.OneFactor = OneFactor; - exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; - exports.OneMinusDstColorFactor = OneMinusDstColorFactor; - exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; - exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; - exports.OrthographicCamera = OrthographicCamera; - exports.PCFShadowMap = PCFShadowMap; - exports.PCFSoftShadowMap = PCFSoftShadowMap; - exports.PMREMGenerator = PMREMGenerator; - exports.ParametricBufferGeometry = ParametricBufferGeometry; - exports.ParametricGeometry = ParametricGeometry; - exports.Particle = Particle; - exports.ParticleBasicMaterial = ParticleBasicMaterial; - exports.ParticleSystem = ParticleSystem; - exports.ParticleSystemMaterial = ParticleSystemMaterial; - exports.Path = Path; - exports.PerspectiveCamera = PerspectiveCamera; - exports.Plane = Plane; - exports.PlaneBufferGeometry = PlaneBufferGeometry; - exports.PlaneGeometry = PlaneGeometry; - exports.PlaneHelper = PlaneHelper; - exports.PointCloud = PointCloud; - exports.PointCloudMaterial = PointCloudMaterial; - exports.PointLight = PointLight; - exports.PointLightHelper = PointLightHelper; - exports.Points = Points; - exports.PointsMaterial = PointsMaterial; - exports.PolarGridHelper = PolarGridHelper; - exports.PolyhedronBufferGeometry = PolyhedronBufferGeometry; - exports.PolyhedronGeometry = PolyhedronGeometry; - exports.PositionalAudio = PositionalAudio; - exports.PropertyBinding = PropertyBinding; - exports.PropertyMixer = PropertyMixer; - exports.QuadraticBezierCurve = QuadraticBezierCurve; - exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; - exports.Quaternion = Quaternion; - exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; - exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; - exports.REVISION = REVISION; - exports.RGBADepthPacking = RGBADepthPacking; - exports.RGBAFormat = RGBAFormat; - exports.RGBAIntegerFormat = RGBAIntegerFormat; - exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; - exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; - exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; - exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; - exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; - exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; - exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; - exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; - exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; - exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; - exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; - exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; - exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; - exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; - exports.RGBA_BPTC_Format = RGBA_BPTC_Format; - exports.RGBA_ETC2_EAC_Format = RGBA_ETC2_EAC_Format; - exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; - exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; - exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; - exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; - exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; - exports.RGBDEncoding = RGBDEncoding; - exports.RGBEEncoding = RGBEEncoding; - exports.RGBEFormat = RGBEFormat; - exports.RGBFormat = RGBFormat; - exports.RGBIntegerFormat = RGBIntegerFormat; - exports.RGBM16Encoding = RGBM16Encoding; - exports.RGBM7Encoding = RGBM7Encoding; - exports.RGB_ETC1_Format = RGB_ETC1_Format; - exports.RGB_ETC2_Format = RGB_ETC2_Format; - exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; - exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; - exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; - exports.RGFormat = RGFormat; - exports.RGIntegerFormat = RGIntegerFormat; - exports.RawShaderMaterial = RawShaderMaterial; - exports.Ray = Ray; - exports.Raycaster = Raycaster; - exports.RectAreaLight = RectAreaLight; - exports.RedFormat = RedFormat; - exports.RedIntegerFormat = RedIntegerFormat; - exports.ReinhardToneMapping = ReinhardToneMapping; - exports.RepeatWrapping = RepeatWrapping; - exports.ReplaceStencilOp = ReplaceStencilOp; - exports.ReverseSubtractEquation = ReverseSubtractEquation; - exports.RingBufferGeometry = RingBufferGeometry; - exports.RingGeometry = RingGeometry; - exports.SRGB8_ALPHA8_ASTC_10x10_Format = SRGB8_ALPHA8_ASTC_10x10_Format; - exports.SRGB8_ALPHA8_ASTC_10x5_Format = SRGB8_ALPHA8_ASTC_10x5_Format; - exports.SRGB8_ALPHA8_ASTC_10x6_Format = SRGB8_ALPHA8_ASTC_10x6_Format; - exports.SRGB8_ALPHA8_ASTC_10x8_Format = SRGB8_ALPHA8_ASTC_10x8_Format; - exports.SRGB8_ALPHA8_ASTC_12x10_Format = SRGB8_ALPHA8_ASTC_12x10_Format; - exports.SRGB8_ALPHA8_ASTC_12x12_Format = SRGB8_ALPHA8_ASTC_12x12_Format; - exports.SRGB8_ALPHA8_ASTC_4x4_Format = SRGB8_ALPHA8_ASTC_4x4_Format; - exports.SRGB8_ALPHA8_ASTC_5x4_Format = SRGB8_ALPHA8_ASTC_5x4_Format; - exports.SRGB8_ALPHA8_ASTC_5x5_Format = SRGB8_ALPHA8_ASTC_5x5_Format; - exports.SRGB8_ALPHA8_ASTC_6x5_Format = SRGB8_ALPHA8_ASTC_6x5_Format; - exports.SRGB8_ALPHA8_ASTC_6x6_Format = SRGB8_ALPHA8_ASTC_6x6_Format; - exports.SRGB8_ALPHA8_ASTC_8x5_Format = SRGB8_ALPHA8_ASTC_8x5_Format; - exports.SRGB8_ALPHA8_ASTC_8x6_Format = SRGB8_ALPHA8_ASTC_8x6_Format; - exports.SRGB8_ALPHA8_ASTC_8x8_Format = SRGB8_ALPHA8_ASTC_8x8_Format; - exports.Scene = Scene; - exports.SceneUtils = SceneUtils; - exports.ShaderChunk = ShaderChunk; - exports.ShaderLib = ShaderLib; - exports.ShaderMaterial = ShaderMaterial; - exports.ShadowMaterial = ShadowMaterial; - exports.Shape = Shape; - exports.ShapeBufferGeometry = ShapeBufferGeometry; - exports.ShapeGeometry = ShapeGeometry; - exports.ShapePath = ShapePath; - exports.ShapeUtils = ShapeUtils; - exports.ShortType = ShortType; - exports.Skeleton = Skeleton; - exports.SkeletonHelper = SkeletonHelper; - exports.SkinnedMesh = SkinnedMesh; - exports.SmoothShading = SmoothShading; - exports.Sphere = Sphere; - exports.SphereBufferGeometry = SphereBufferGeometry; - exports.SphereGeometry = SphereGeometry; - exports.Spherical = Spherical; - exports.SphericalHarmonics3 = SphericalHarmonics3; - exports.SphericalReflectionMapping = SphericalReflectionMapping; - exports.Spline = Spline; - exports.SplineCurve = SplineCurve; - exports.SplineCurve3 = SplineCurve3; - exports.SpotLight = SpotLight; - exports.SpotLightHelper = SpotLightHelper; - exports.SpotLightShadow = SpotLightShadow; - exports.Sprite = Sprite; - exports.SpriteMaterial = SpriteMaterial; - exports.SrcAlphaFactor = SrcAlphaFactor; - exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; - exports.SrcColorFactor = SrcColorFactor; - exports.StaticCopyUsage = StaticCopyUsage; - exports.StaticDrawUsage = StaticDrawUsage; - exports.StaticReadUsage = StaticReadUsage; - exports.StereoCamera = StereoCamera; - exports.StreamCopyUsage = StreamCopyUsage; - exports.StreamDrawUsage = StreamDrawUsage; - exports.StreamReadUsage = StreamReadUsage; - exports.StringKeyframeTrack = StringKeyframeTrack; - exports.SubtractEquation = SubtractEquation; - exports.SubtractiveBlending = SubtractiveBlending; - exports.TOUCH = TOUCH; - exports.TangentSpaceNormalMap = TangentSpaceNormalMap; - exports.TetrahedronBufferGeometry = TetrahedronBufferGeometry; - exports.TetrahedronGeometry = TetrahedronGeometry; - exports.TextBufferGeometry = TextBufferGeometry; - exports.TextGeometry = TextGeometry; - exports.Texture = Texture; - exports.TextureLoader = TextureLoader; - exports.TorusBufferGeometry = TorusBufferGeometry; - exports.TorusGeometry = TorusGeometry; - exports.TorusKnotBufferGeometry = TorusKnotBufferGeometry; - exports.TorusKnotGeometry = TorusKnotGeometry; - exports.Triangle = Triangle; - exports.TriangleFanDrawMode = TriangleFanDrawMode; - exports.TriangleStripDrawMode = TriangleStripDrawMode; - exports.TrianglesDrawMode = TrianglesDrawMode; - exports.TubeBufferGeometry = TubeBufferGeometry; - exports.TubeGeometry = TubeGeometry; - exports.UVMapping = UVMapping; - exports.Uint16Attribute = Uint16Attribute; - exports.Uint16BufferAttribute = Uint16BufferAttribute; - exports.Uint32Attribute = Uint32Attribute; - exports.Uint32BufferAttribute = Uint32BufferAttribute; - exports.Uint8Attribute = Uint8Attribute; - exports.Uint8BufferAttribute = Uint8BufferAttribute; - exports.Uint8ClampedAttribute = Uint8ClampedAttribute; - exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; - exports.Uncharted2ToneMapping = Uncharted2ToneMapping; - exports.Uniform = Uniform; - exports.UniformsLib = UniformsLib; - exports.UniformsUtils = UniformsUtils; - exports.UnsignedByteType = UnsignedByteType; - exports.UnsignedInt248Type = UnsignedInt248Type; - exports.UnsignedIntType = UnsignedIntType; - exports.UnsignedShort4444Type = UnsignedShort4444Type; - exports.UnsignedShort5551Type = UnsignedShort5551Type; - exports.UnsignedShort565Type = UnsignedShort565Type; - exports.UnsignedShortType = UnsignedShortType; - exports.VSMShadowMap = VSMShadowMap; - exports.Vector2 = Vector2; - exports.Vector3 = Vector3; - exports.Vector4 = Vector4; - exports.VectorKeyframeTrack = VectorKeyframeTrack; - exports.Vertex = Vertex; - exports.VertexColors = VertexColors; - exports.VideoTexture = VideoTexture; - exports.WebGLCubeRenderTarget = WebGLCubeRenderTarget; - exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; - exports.WebGLRenderTarget = WebGLRenderTarget; - exports.WebGLRenderTargetCube = WebGLRenderTargetCube; - exports.WebGLRenderer = WebGLRenderer; - exports.WebGLUtils = WebGLUtils; - exports.WireframeGeometry = WireframeGeometry; - exports.WireframeHelper = WireframeHelper; - exports.WrapAroundEnding = WrapAroundEnding; - exports.XHRLoader = XHRLoader; - exports.ZeroCurvatureEnding = ZeroCurvatureEnding; - exports.ZeroFactor = ZeroFactor; - exports.ZeroSlopeEnding = ZeroSlopeEnding; - exports.ZeroStencilOp = ZeroStencilOp; - exports.sRGBEncoding = sRGBEncoding; - - Object.defineProperty(exports, '__esModule', { value: true }); - -}))); \ No newline at end of file From a9eb1603aa55652d252f6d7df26d4479f8ccd66f Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 04:59:49 +0100 Subject: [PATCH 014/166] frontend: undo/redo scene commands (Phase 10) Implement the 8 Command classes in src/commands/scene.ts, each capturing before/after state on the Phase 9 Three.js object model so CommandHistory can undo/redo: - ChangeNodeColorCommand / ChangeNodeSizeCommand - ChangeEdgeColorCommand (captures colors + edgeFileColorPriority) - MoveLayerCommand (position/rotation/scale; redraws inter-layer edges) - ApplyLayoutCommand (positions + optional cluster IDs/colors, one step) - ApplyTopologyCommand (node scales) - ChangeThemeCommand (store-only via injected getter/setter) - LoadNetworkCommand (build closure + ctx registry snapshot for undo) Commands are thin: they apply/reverse already-decided values. API fetch and position/scale math is the Phase 11 actions' job, which will construct these commands with the results. Supporting changes: - runtime.ts: add ctx.edgeObjects registry + snapshotRegistries / restoreRegistries helpers (RegistrySnapshot). - bus: add layer:moved event. 9 Vitest tests (execute/undo/redo per command). tsc + eslint clean; 30/30 tests pass. Co-Authored-By: Claude Fable 5 --- PLAN.md | 20 +- frontend/src/bus/index.ts | 11 +- frontend/src/commands/base.ts | 5 +- frontend/src/commands/scene.test.ts | 173 +++++++++++++++ frontend/src/commands/scene.ts | 327 ++++++++++++++++++++++++++++ frontend/src/three/runtime.ts | 44 ++++ 6 files changed, 568 insertions(+), 12 deletions(-) create mode 100644 frontend/src/commands/scene.test.ts create mode 100644 frontend/src/commands/scene.ts diff --git a/PLAN.md b/PLAN.md index 737b9da..b47fcbc 100644 --- a/PLAN.md +++ b/PLAN.md @@ -122,15 +122,17 @@ Clustering in v2 is not a standalone action — it is an option of the layout ru ## Phase 10 — Frontend: Commands -- [ ] Implement `LoadNetworkCommand` -- [ ] Implement `ApplyLayoutCommand` — captures node positions before/after, plus cluster IDs/colors when the layout ran with clustering (single command: one API response, one undo step) -- [ ] Implement `ApplyTopologyCommand` — captures node scale values before/after -- [ ] Implement `MoveLayerCommand` — captures layer transform before/after -- [ ] Implement `ChangeNodeColorCommand` -- [ ] Implement `ChangeNodeSizeCommand` -- [ ] Implement `ChangeEdgeColorCommand` -- [ ] Implement `ChangeThemeCommand` -- [ ] Write Vitest tests for execute/undo/redo on each command +All 8 in `src/commands/scene.ts`. Commands are **thin**: they apply/reverse already-decided values on the Phase 9 object model (via `ctx`); API fetch + position/scale math is the Phase 11 actions' job, which construct these with the results. Added `ctx.edgeObjects` registry + `snapshotRegistries`/`restoreRegistries` helpers to `runtime.ts`, and a `layer:moved` bus event. + +- [x] Implement `LoadNetworkCommand` — takes a `build: () => void` closure; snapshots ctx registries before/after so a load is one undo step (concrete builder lands in Phase 11 network action) +- [x] Implement `ApplyLayoutCommand` — captures node positions before/after, plus cluster IDs/colors + `nodeColorPrioritySource` when clustering ran (single command, one undo step) +- [x] Implement `ApplyTopologyCommand` — captures node scale values before/after +- [x] Implement `MoveLayerCommand` — captures layer position/rotation/scale before/after; redraws inter-layer edges +- [x] Implement `ChangeNodeColorCommand` +- [x] Implement `ChangeNodeSizeCommand` +- [x] Implement `ChangeEdgeColorCommand` — captures colors/importedColors + `edgeFileColorPriority`, redraws +- [x] Implement `ChangeThemeCommand` — store-only (getter/setter injected); concrete recolour is the Phase 11 themes action listening to `theme:changed` +- [x] Write Vitest tests for execute/undo/redo on each command (9 tests in `src/commands/scene.test.ts`) --- diff --git a/frontend/src/bus/index.ts b/frontend/src/bus/index.ts index 61757c5..a710249 100644 --- a/frontend/src/bus/index.ts +++ b/frontend/src/bus/index.ts @@ -9,6 +9,7 @@ export interface BusEvents { 'layout:applied': { positions: Record } 'clustering:applied': { clusters: Record } 'topology:applied': { scales: Record } + 'layer:moved': { layerIndex: number } 'theme:changed': { theme: string } 'history:changed': { canUndo: boolean; canRedo: boolean } } @@ -19,7 +20,10 @@ class EventBus { // one Set of handlers per event name; typed via the public method signatures private handlers = new Map>>() - on(event: K, handler: Handler): () => void { + on( + event: K, + handler: Handler + ): () => void { let set = this.handlers.get(event) if (!set) { set = new Set() @@ -29,7 +33,10 @@ class EventBus { return () => this.off(event, handler) } - off(event: K, handler: Handler): void { + off( + event: K, + handler: Handler + ): void { this.handlers.get(event)?.delete(handler as Handler) } diff --git a/frontend/src/commands/base.ts b/frontend/src/commands/base.ts index 696a9c7..35c0e17 100644 --- a/frontend/src/commands/base.ts +++ b/frontend/src/commands/base.ts @@ -45,7 +45,10 @@ class CommandHistory { } private notify(): void { - bus.emit('history:changed', { canUndo: this.canUndo, canRedo: this.canRedo }) + bus.emit('history:changed', { + canUndo: this.canUndo, + canRedo: this.canRedo, + }) } } diff --git a/frontend/src/commands/scene.test.ts b/frontend/src/commands/scene.test.ts new file mode 100644 index 0000000..5ed5a68 --- /dev/null +++ b/frontend/src/commands/scene.test.ts @@ -0,0 +1,173 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import { Node, Layer, Scene, Edge, ctx, resetContext } from '../three' +import { + ChangeNodeColorCommand, + ChangeNodeSizeCommand, + ChangeEdgeColorCommand, + MoveLayerCommand, + ApplyLayoutCommand, + ApplyTopologyCommand, + ChangeThemeCommand, + LoadNetworkCommand, +} from './scene' + +// Minimal populated scene: one layer "L", nodes A/B, one intra edge A->B. +function seed(): void { + const scene = new Scene() + const layer = new Layer({ geometry_parameters_width: 100 }) + ctx.scene = scene + ctx.layers = [layer] + ctx.layerGroups = { L: 0 } + ctx.nodeGroups = { 'A::L': 'L', 'B::L': 'L' } + ctx.nodeLayerNames = ['A::L', 'B::L'] + ctx.nodeObjects = [ + new Node({ + name: 'A', + nodeLayerName: 'A::L', + layer: 'L', + color: '#111111', + }), + new Node({ + name: 'B', + nodeLayerName: 'B::L', + layer: 'L', + color: '#222222', + }), + ] + ctx.edgeObjects = [new Edge({ source: 'A::L', target: 'B::L', weights: [1] })] +} + +beforeEach(() => { + resetContext() + seed() +}) + +describe('ChangeNodeColorCommand', () => { + it('executes, undoes and redoes', () => { + const c = new ChangeNodeColorCommand(0, '#00ff00') + c.execute() + expect(ctx.nodeObjects[0].color).toBe('#00ff00') + c.undo() + expect(ctx.nodeObjects[0].color).toBe('#111111') + c.execute() // redo + expect(ctx.nodeObjects[0].color).toBe('#00ff00') + }) +}) + +describe('ChangeNodeSizeCommand', () => { + it('executes and undoes', () => { + const c = new ChangeNodeSizeCommand(1, 3) + c.execute() + expect(ctx.nodeObjects[1].getScale()).toBe(3) + c.undo() + expect(ctx.nodeObjects[1].getScale()).toBe(1) + }) +}) + +describe('ChangeEdgeColorCommand', () => { + it('recolours the edge and restores colour + priority on undo', () => { + expect(ctx.edgeFileColorPriority).toBe(false) + const c = new ChangeEdgeColorCommand(0, '#abcdef') + c.execute() + expect(ctx.edgeObjects[0].colors).toEqual(['#abcdef']) + expect(ctx.edgeObjects[0].importedColors).toEqual(['#abcdef']) + expect(ctx.edgeFileColorPriority).toBe(true) + c.undo() + expect(ctx.edgeObjects[0].colors).toEqual([ctx.edgeDefaultColor]) + expect(ctx.edgeFileColorPriority).toBe(false) + }) +}) + +describe('MoveLayerCommand', () => { + it('applies then reverts a layer transform', () => { + const c = new MoveLayerCommand(0, { + position: { x: 50 }, + rotation: { y: 0.3 }, + }) + c.execute() + expect(ctx.layers[0].getPosition('x')).toBe(50) + expect(ctx.layers[0].getRotation('y')).toBeCloseTo(0.3) + c.undo() + expect(ctx.layers[0].getPosition('x')).toBe(0) + expect(ctx.layers[0].getRotation('y')).toBe(0) + }) +}) + +describe('ApplyLayoutCommand', () => { + it('places nodes and restores previous positions', () => { + const c = new ApplyLayoutCommand({ 'A::L': [5, 6], 'B::L': [-5, -6] }) + c.execute() + expect(ctx.nodeObjects[0].getPosition('y')).toBe(5) + expect(ctx.nodeObjects[0].getPosition('z')).toBe(6) + expect(ctx.nodeObjects[0].getPosition('x')).toBe(0) + c.undo() + expect(ctx.nodeObjects[0].getPosition('y')).toBe(0) + expect(ctx.nodeObjects[0].getPosition('z')).toBe(0) + }) + + it('assigns clusters + colours and reverts them', () => { + const c = new ApplyLayoutCommand( + { 'A::L': [1, 1], 'B::L': [2, 2] }, + { 'A::L': 0, 'B::L': 1 } + ) + c.execute() + expect(ctx.nodeObjects[0].getCluster()).toBe('0') + expect(ctx.nodeColorPrioritySource).toBe('cluster') + c.undo() + expect(ctx.nodeObjects[0].getCluster()).toBe('') + expect(ctx.nodeColorPrioritySource).toBe('default') + }) +}) + +describe('ApplyTopologyCommand', () => { + it('scales nodes and restores', () => { + const c = new ApplyTopologyCommand({ 'A::L': 2.5, 'B::L': 0.5 }) + c.execute() + expect(ctx.nodeObjects[0].getScale()).toBe(2.5) + expect(ctx.nodeObjects[1].getScale()).toBe(0.5) + c.undo() + expect(ctx.nodeObjects[0].getScale()).toBe(1) + expect(ctx.nodeObjects[1].getScale()).toBe(1) + }) +}) + +describe('ChangeThemeCommand', () => { + it('drives a store setter and reverts', () => { + let theme = 'dark' + const c = new ChangeThemeCommand( + 'light', + () => theme, + (t) => (theme = t) + ) + c.execute() + expect(theme).toBe('light') + c.undo() + expect(theme).toBe('dark') + }) +}) + +describe('LoadNetworkCommand', () => { + it('builds on execute, clears on undo, rebuilds on redo', () => { + // Start from an empty context so we can observe the load. + resetContext() + const build = (): void => { + ctx.scene = new Scene() + ctx.layers = [new Layer({ geometry_parameters_width: 100 })] + ctx.nodeLayerNames = ['X::L'] + ctx.nodeGroups = { 'X::L': 'L' } + ctx.layerGroups = { L: 0 } + ctx.nodeObjects = [ + new Node({ name: 'X', nodeLayerName: 'X::L', layer: 'L' }), + ] + ctx.edgeObjects = [] + } + const c = new LoadNetworkCommand(build) + c.execute() + expect(ctx.nodeObjects).toHaveLength(1) + c.undo() + expect(ctx.nodeObjects).toHaveLength(0) + expect(ctx.scene).toBeNull() + c.execute() // redo + expect(ctx.nodeObjects).toHaveLength(1) + }) +}) diff --git a/frontend/src/commands/scene.ts b/frontend/src/commands/scene.ts new file mode 100644 index 0000000..c3d4e71 --- /dev/null +++ b/frontend/src/commands/scene.ts @@ -0,0 +1,327 @@ +// Scene-mutation commands (SPEC §4). Each captures before/after state on the +// Phase 9 Three.js object model (via ctx) so CommandHistory can undo/redo. +// +// Commands are thin: they apply/reverse already-decided values. Fetching from +// the API and computing final positions/scales is the job of the Phase 11 +// actions, which construct these commands with the results. + +import { bus } from '../bus' +import type { Command } from './base' +import { + ctx, + COLOR_VECTOR_280, + snapshotRegistries, + restoreRegistries, + type RegistrySnapshot, +} from '../three' + +type Vec3 = { x: number; y: number; z: number } + +function clusterColor(id: number): string { + return COLOR_VECTOR_280[id % COLOR_VECTOR_280.length] +} + +function redrawIntraLayerEdges(): void { + for (const e of ctx.edgeObjects) if (!e.interLayer) e.redrawEdge() +} + +function redrawInterLayerEdges(): void { + for (const e of ctx.edgeObjects) if (e.interLayer) e.redrawEdge() +} + +export class ChangeNodeColorCommand implements Command { + description: string + private readonly oldColor: string + + constructor( + private readonly nodeIndex: number, + private readonly newColor: string + ) { + this.oldColor = ctx.nodeObjects[nodeIndex].color + this.description = `Change node color to ${newColor}` + } + + private apply(color: string): void { + const node = ctx.nodeObjects[this.nodeIndex] + node.setColor(color) + bus.emit('node:color-changed', { nodeId: node.nodeLayerName, color }) + } + + execute(): void { + this.apply(this.newColor) + } + undo(): void { + this.apply(this.oldColor) + } +} + +export class ChangeNodeSizeCommand implements Command { + description: string + private readonly oldSize: number + + constructor( + private readonly nodeIndex: number, + private readonly newSize: number + ) { + this.oldSize = ctx.nodeObjects[nodeIndex].getScale() + this.description = `Change node size to ${newSize}` + } + + private apply(size: number): void { + const node = ctx.nodeObjects[this.nodeIndex] + node.setScale(size) + bus.emit('node:size-changed', { nodeId: node.nodeLayerName, size }) + } + + execute(): void { + this.apply(this.newSize) + } + undo(): void { + this.apply(this.oldSize) + } +} + +export class ChangeEdgeColorCommand implements Command { + description: string + private readonly oldColors: string[] + private readonly oldImported: string[] + private readonly oldPriority: boolean + + constructor( + private readonly edgeIndex: number, + private readonly newColor: string + ) { + const edge = ctx.edgeObjects[edgeIndex] + this.oldColors = edge.colors.slice() + this.oldImported = edge.importedColors.slice() + this.oldPriority = ctx.edgeFileColorPriority + this.description = `Change edge color to ${newColor}` + } + + execute(): void { + const edge = ctx.edgeObjects[this.edgeIndex] + edge.colors = edge.colors.map(() => this.newColor) + edge.importedColors = edge.importedColors.map(() => this.newColor) + // v2: recolouring an edge switches the scene into file-colour-priority mode. + ctx.edgeFileColorPriority = true + edge.redrawEdge() + bus.emit('edge:color-changed', { edgeId: edge.name, color: this.newColor }) + } + + undo(): void { + const edge = ctx.edgeObjects[this.edgeIndex] + edge.colors = this.oldColors.slice() + edge.importedColors = this.oldImported.slice() + ctx.edgeFileColorPriority = this.oldPriority + edge.redrawEdge() + bus.emit('edge:color-changed', { + edgeId: edge.name, + color: this.oldColors[0], + }) + } +} + +export interface LayerTransform { + position?: Partial + rotation?: Partial + scale?: number +} + +export class MoveLayerCommand implements Command { + description: string + private readonly before: Required< + Omit + > & { + position: Vec3 + rotation: Vec3 + } + + constructor( + private readonly layerIndex: number, + private readonly after: LayerTransform + ) { + const l = ctx.layers[layerIndex] + this.before = { + position: { + x: l.getPosition('x'), + y: l.getPosition('y'), + z: l.getPosition('z'), + }, + rotation: { + x: l.getRotation('x'), + y: l.getRotation('y'), + z: l.getRotation('z'), + }, + scale: l.getScale(), + } + this.description = `Move layer ${l.getName()}` + } + + private applyTransform(t: LayerTransform): void { + const l = ctx.layers[this.layerIndex] + if (t.position) + for (const [axis, v] of Object.entries(t.position)) + l.setPosition(axis as 'x' | 'y' | 'z', v) + if (t.rotation) + for (const [axis, v] of Object.entries(t.rotation)) + l.setRotation(axis as 'x' | 'y' | 'z', v) + if (t.scale !== undefined) l.setScale(t.scale) + redrawInterLayerEdges() + bus.emit('layer:moved', { layerIndex: this.layerIndex }) + } + + execute(): void { + this.applyTransform(this.after) + } + undo(): void { + this.applyTransform(this.before) + } +} + +export class ApplyLayoutCommand implements Command { + description = 'Apply layout' + private readonly beforePos: Record = {} + private readonly beforeCluster: Record< + string, + { cluster: string; color: string } + > = {} + private readonly beforePriority = ctx.nodeColorPrioritySource + + constructor( + private readonly positions: Record, + private readonly clusters: Record | null = null + ) { + for (const name of Object.keys(positions)) { + const node = ctx.nodeObjects[ctx.nodeLayerNames.indexOf(name)] + this.beforePos[name] = [node.getPosition('y'), node.getPosition('z')] + if (clusters) + this.beforeCluster[name] = { + cluster: node.cluster, + color: node.clusterColor, + } + } + } + + private place(positions: Record): void { + for (const [name, [y, z]] of Object.entries(positions)) { + const node = ctx.nodeObjects[ctx.nodeLayerNames.indexOf(name)] + node.setPosition('x', 0) + node.setPosition('y', y) + node.setPosition('z', z) + } + } + + execute(): void { + this.place(this.positions) + if (this.clusters) { + for (const [name, id] of Object.entries(this.clusters)) { + const node = ctx.nodeObjects[ctx.nodeLayerNames.indexOf(name)] + node.setCluster(String(id)) + node.setColor(clusterColor(id), false, true /* clusterMode */) + } + ctx.nodeColorPrioritySource = 'cluster' + bus.emit('clustering:applied', { clusters: this.clusters }) + } + redrawIntraLayerEdges() + bus.emit('layout:applied', { positions: this.positions }) + } + + undo(): void { + this.place(this.beforePos) + if (this.clusters) { + for (const [name, prev] of Object.entries(this.beforeCluster)) { + const node = ctx.nodeObjects[ctx.nodeLayerNames.indexOf(name)] + node.setCluster(prev.cluster) + node.setColor(prev.color, false, true) + } + ctx.nodeColorPrioritySource = this.beforePriority + } + redrawIntraLayerEdges() + bus.emit('layout:applied', { positions: this.beforePos }) + } +} + +export class ApplyTopologyCommand implements Command { + description = 'Apply topology metric' + private readonly before: Record = {} + + constructor(private readonly scales: Record) { + for (const name of Object.keys(scales)) + this.before[name] = + ctx.nodeObjects[ctx.nodeLayerNames.indexOf(name)].getScale() + } + + private apply(scales: Record): void { + for (const [name, s] of Object.entries(scales)) + ctx.nodeObjects[ctx.nodeLayerNames.indexOf(name)].setScale(s) + bus.emit('topology:applied', { scales }) + } + + execute(): void { + this.apply(this.scales) + } + undo(): void { + this.apply(this.before) + } +} + +export class ChangeThemeCommand implements Command { + description: string + private readonly oldTheme: string + + // Store-only: the concrete recolour (renderer clear color, floor/edge/label + // colors) is applied by the Phase 11 themes action listening to theme:changed. + constructor( + private readonly newTheme: string, + getTheme: () => string, + private readonly setTheme: (t: string) => void + ) { + this.oldTheme = getTheme() + this.description = `Change theme to ${newTheme}` + } + + private apply(theme: string): void { + this.setTheme(theme) + bus.emit('theme:changed', { theme }) + } + + execute(): void { + this.apply(this.newTheme) + } + undo(): void { + this.apply(this.oldTheme) + } +} + +export class LoadNetworkCommand implements Command { + description = 'Load network' + private prev: RegistrySnapshot | null = null + private next: RegistrySnapshot | null = null + + // `build` populates ctx with the new scene graph (concrete builder lands in + // Phase 11 network action). The command wraps it with a reversible snapshot. + constructor(private readonly build: () => void) {} + + private emitLoaded(): void { + bus.emit('network:loaded', { + nodeCount: ctx.nodeObjects.length, + edgeCount: ctx.edgeObjects.length, + }) + } + + execute(): void { + if (this.next === null) { + this.prev = snapshotRegistries() + this.build() + this.next = snapshotRegistries() + } else { + restoreRegistries(this.next) + } + this.emitLoaded() + } + + undo(): void { + if (this.prev) restoreRegistries(this.prev) + this.emitLoaded() + } +} diff --git a/frontend/src/three/runtime.ts b/frontend/src/three/runtime.ts index dae2194..c520d65 100644 --- a/frontend/src/three/runtime.ts +++ b/frontend/src/three/runtime.ts @@ -13,6 +13,7 @@ import type { Scene } from './Scene' import type { Layer } from './Layer' import type { Node } from './Node' +import type { Edge } from './Edge' export type ColorPrioritySource = 'default' | 'picker' | 'cluster' @@ -26,6 +27,7 @@ export interface RuntimeContext { scene: Scene | null layers: Layer[] nodeObjects: Node[] + edgeObjects: Edge[] nodeLayerNames: string[] nodeGroups: Record // nodeLayerName -> layerName layerGroups: Record // layerName -> layer index @@ -60,6 +62,7 @@ export const ctx: RuntimeContext = { scene: null, layers: [], nodeObjects: [], + edgeObjects: [], nodeLayerNames: [], nodeGroups: {}, layerGroups: {}, @@ -90,6 +93,7 @@ export function resetContext(): void { ctx.scene = null ctx.layers = [] ctx.nodeObjects = [] + ctx.edgeObjects = [] ctx.nodeLayerNames = [] ctx.nodeGroups = {} ctx.layerGroups = {} @@ -109,3 +113,43 @@ export function resetContext(): void { ctx.layerColorPrioritySource = 'default' ctx.nodeColorPrioritySource = 'default' } + +// Registry snapshot — the mutable scene-graph state a network load replaces. +// LoadNetworkCommand snapshots before/after so a load is one undo step. +export interface RegistrySnapshot { + scene: Scene | null + layers: Layer[] + nodeObjects: Node[] + edgeObjects: Edge[] + nodeLayerNames: string[] + nodeGroups: Record + layerGroups: Record + channelColors: Record + channelVisibility: Record +} + +export function snapshotRegistries(): RegistrySnapshot { + return { + scene: ctx.scene, + layers: ctx.layers, + nodeObjects: ctx.nodeObjects, + edgeObjects: ctx.edgeObjects, + nodeLayerNames: ctx.nodeLayerNames, + nodeGroups: ctx.nodeGroups, + layerGroups: ctx.layerGroups, + channelColors: ctx.channelColors, + channelVisibility: ctx.channelVisibility, + } +} + +export function restoreRegistries(s: RegistrySnapshot): void { + ctx.scene = s.scene + ctx.layers = s.layers + ctx.nodeObjects = s.nodeObjects + ctx.edgeObjects = s.edgeObjects + ctx.nodeLayerNames = s.nodeLayerNames + ctx.nodeGroups = s.nodeGroups + ctx.layerGroups = s.layerGroups + ctx.channelColors = s.channelColors + ctx.channelVisibility = s.channelVisibility +} From a582bd18f45a18f73ed03239984b0905a6f0f306 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 05:04:31 +0100 Subject: [PATCH 015/166] frontend: layout/topology actions + Phase 11 re-scope (Phase 11 partial) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port the portable, unit-testable slice of the v2 object_actions: src/actions/layout.ts — executeLayout coordinate normalization plus applyLayout / applyTopology, which fetch from the API and dispatch the Phase 10 ApplyLayoutCommand / ApplyTopologyCommand (one undo step each). Confirmed against backend: /api/layout returns raw igraph [y,z] coords (no layer-width normalization), so the frontend still performs the v2 mapping into [-minWidth/2, minWidth/2] * layer scale. Topology scales are already mapped server-side and applied verbatim. Re-scope the rest of Phase 11: the remaining 9 action files are coupled to the renderer, camera, animate() loop, raycaster, DragControls, CSS2D labels, and the UI panels — all built in Phases 12-13. They move to those phases (documented in PLAN.md) rather than being stubbed now, and www/js/object_actions/ deletion moves with them. ponytail: allLayers scope only; perLayer/local layout refinements deferred to the Phase 13 scope UI. 5 Vitest tests (normalize + applyLayout + applyTopology). tsc + eslint clean; 35/35 tests pass. Co-Authored-By: Claude Fable 5 --- PLAN.md | 27 +++++---- frontend/src/actions/layout.test.ts | 87 +++++++++++++++++++++++++++++ frontend/src/actions/layout.ts | 60 ++++++++++++++++++++ 3 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 frontend/src/actions/layout.test.ts create mode 100644 frontend/src/actions/layout.ts diff --git a/PLAN.md b/PLAN.md index b47fcbc..9a025a6 100644 --- a/PLAN.md +++ b/PLAN.md @@ -138,16 +138,23 @@ All 8 in `src/commands/scene.ts`. Commands are **thin**: they apply/reverse alre ## Phase 11 — Frontend: Object Actions -- [ ] Migrate `canvas_controls.js` → `src/actions/canvas_controls.ts` -- [ ] Migrate `layout.js` → `src/actions/layout.ts` -- [ ] Migrate `node.js` → `src/actions/node.ts` -- [ ] Migrate `edge.js` → `src/actions/edge.ts` -- [ ] Migrate `layer.js` → `src/actions/layer.ts` -- [ ] Migrate `network.js` → `src/actions/network.ts` -- [ ] Migrate `screen.js` → `src/actions/screen.ts` -- [ ] Migrate `themes.js` → `src/actions/themes.ts` -- [ ] Migrate `labels.js` → `src/actions/labels.ts` -- [ ] Migrate `right_click_menu.js` → `src/actions/right_click_menu.ts` +**Re-scoped.** The v2 actions split cleanly into two groups by dependency: +- **Pure data-model actions** (no renderer/camera/animate/DOM) — portable + unit-testable now. +- **Render-loop / DOM / event actions** — need the renderer, camera, `animate()` loop, raycaster, DragControls, CSS2D labels, and the UI panels. Those dependencies are built in **Phase 12** (main loop, `screen.js` renderer/camera, `event_listeners.js`) and **Phase 13** (UI panels). Migrating them now would mean stubbing all of that, so they move to the phase where their deps exist. + +Done now (portable core): +- [x] Migrate `layout.js` → `src/actions/layout.ts` — `executeLayout` coordinate normalization (backend returns raw igraph `[y,z]`, confirmed) + `applyLayout` / `applyTopology` dispatching the Phase 10 commands. 5 Vitest tests. *(allLayers scope; perLayer/local refinements deferred to Phase 13 scope UI.)* + +Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2D) and Phase 13 (UI/DOM), then delete `www/js/object_actions/`: +- [ ] `screen.js` → `src/actions/screen.ts` (renderer, camera, window bounds, raycaster, `animate()`) +- [ ] `network.js` → `src/actions/network.ts` (buildNetwork orchestrator; feeds `LoadNetworkCommand`) +- [ ] `node.js` → `src/actions/node.ts` (build + raycaster hover/selection; color/size actions) +- [ ] `edge.js` → `src/actions/edge.ts` (build + inter/intra render toggles) +- [ ] `layer.js` → `src/actions/layer.ts` (spread/move + DragControls + layer checkboxes) +- [ ] `themes.js` → `src/actions/themes.ts` (needs renderer `setClearColor`; listens to `theme:changed`) +- [ ] `labels.js` → `src/actions/labels.ts` (CSS2D label rendering in the animate loop) +- [ ] `canvas_controls.js` → `src/actions/canvas_controls.ts` (mouse/keyboard canvas controls, 510 lines, heavy DOM) +- [ ] `right_click_menu.js` → `src/actions/right_click_menu.ts` (DOM context menu) - [ ] Delete `www/js/object_actions/` --- diff --git a/frontend/src/actions/layout.test.ts b/frontend/src/actions/layout.test.ts new file mode 100644 index 0000000..784882b --- /dev/null +++ b/frontend/src/actions/layout.test.ts @@ -0,0 +1,87 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { Node, Layer, Scene, ctx, resetContext } from '../three' +import { api } from '../api/client' +import { history } from '../commands/base' +import { normalizeLayoutPositions, applyLayout, applyTopology } from './layout' + +// Two layers of different widths; one node each. +function seed(): void { + ctx.scene = new Scene() + ctx.layers = [ + new Layer({ id: 0, name: 'L1', geometry_parameters_width: 100 }), + new Layer({ id: 1, name: 'L2', geometry_parameters_width: 400 }), + ] + ctx.layerGroups = { L1: 0, L2: 1 } + ctx.nodeGroups = { 'A::L1': 'L1', 'B::L1': 'L1', 'C::L2': 'L2' } + ctx.nodeLayerNames = ['A::L1', 'B::L1', 'C::L2'] + ctx.nodeObjects = [ + new Node({ name: 'A', nodeLayerName: 'A::L1', layer: 'L1' }), + new Node({ name: 'B', nodeLayerName: 'B::L1', layer: 'L1' }), + new Node({ name: 'C', nodeLayerName: 'C::L2', layer: 'L2' }), + ] + ctx.edgeObjects = [] +} + +beforeEach(() => { + resetContext() + seed() +}) + +describe('normalizeLayoutPositions', () => { + it('maps coords into [-minWidth/2, minWidth/2] (minWidth = 100 -> [-50, 50])', () => { + // Two L1 nodes span the full y range -> min maps to -50, max to +50. + const out = normalizeLayoutPositions({ 'A::L1': [0, 0], 'B::L1': [10, 10] }) + expect(out['A::L1']).toEqual([-50, -50]) + expect(out['B::L1']).toEqual([50, 50]) + }) + + it('collapses a degenerate (single-value) axis to 0', () => { + const out = normalizeLayoutPositions({ 'A::L1': [5, 5] }) + expect(out['A::L1']).toEqual([0, 0]) + }) + + it('multiplies by the node layer scale', () => { + ctx.layers[0].setScale(2) + const out = normalizeLayoutPositions({ 'A::L1': [0, 0], 'B::L1': [10, 10] }) + expect(out['B::L1']).toEqual([100, 100]) // 50 * scale 2 + }) +}) + +describe('applyLayout', () => { + it('fetches, normalizes, and pushes one ApplyLayoutCommand', async () => { + const spy = vi + .spyOn(api, 'layout') + .mockResolvedValue({ + positions: { 'A::L1': [0, 0], 'B::L1': [10, 10] }, + clusters: null, + }) + await applyLayout({ + nodes: [], + edges: [], + algorithm: 'DrL', + selected_layers: ['L1'], + }) + expect(spy).toHaveBeenCalledOnce() + expect(ctx.nodeObjects[0].getPosition('y')).toBe(-50) + expect(ctx.nodeObjects[1].getPosition('y')).toBe(50) + expect(history.canUndo).toBe(true) + spy.mockRestore() + }) +}) + +describe('applyTopology', () => { + it('fetches and applies scales verbatim (already mapped server-side)', async () => { + const spy = vi + .spyOn(api, 'topology') + .mockResolvedValue({ scales: { 'A::L1': 1.7 }, raw: { 'A::L1': 12 } }) + await applyTopology({ + nodes: [], + edges: [], + metric: 'Degree', + selected_layers: ['L1'], + }) + expect(spy).toHaveBeenCalledOnce() + expect(ctx.nodeObjects[0].getScale()).toBe(1.7) + spy.mockRestore() + }) +}) diff --git a/frontend/src/actions/layout.ts b/frontend/src/actions/layout.ts new file mode 100644 index 0000000..8f00cb1 --- /dev/null +++ b/frontend/src/actions/layout.ts @@ -0,0 +1,60 @@ +// Layout + topology actions — port of v2 www/js/object_actions/layout.js +// (executeLayout / scaleTopology). Fetches from the API, normalizes the raw +// igraph coordinates into each node's layer plane, and dispatches the Phase 10 +// commands so the change is one undo step. + +import { api, type LayoutRequest, type TopologyRequest } from '../api/client' +import { ctx } from '../three' +import { history } from '../commands/base' +import { ApplyLayoutCommand, ApplyTopologyCommand } from '../commands/scene' + +// v2 executeLayout: the backend returns raw 2D [y, z] coords; map them into the +// layer plane, target range [-minWidth/2, minWidth/2], then scale by the node's +// own layer scale. x is always 0 (position on the floor). +// +// ponytail: implements the allLayers scope (one global min/max + the narrowest +// layer width as target). The perLayer / local-layout refinements (v2's +// perLayerLayoutFLag / localLayoutFlag, which re-run per layer or preserve the +// current sub-region) are deferred until Phase 13 wires the scope UI. +export function normalizeLayoutPositions( + raw: Record +): Record { + const names = Object.keys(raw) + if (names.length === 0) return {} + + const ys = names.map((n) => raw[n][0]) + const zs = names.map((n) => raw[n][1]) + const yMin = Math.min(...ys) + const yMax = Math.max(...ys) + const zMin = Math.min(...zs) + const zMax = Math.max(...zs) + + const minWidth = Math.min( + ...ctx.layers.map((l) => l.geometry_parameters_width) + ) + const t = minWidth / 2 // target range is [-t, t] + + const out: Record = {} + for (const name of names) { + const [y, z] = raw[name] + const scale = ctx.layers[ctx.layerGroups[ctx.nodeGroups[name]]].getScale() + const ny = + yMax - yMin !== 0 ? ((y - yMin) * (2 * t)) / (yMax - yMin) - t : 0 + const nz = + zMax - zMin !== 0 ? ((z - zMin) * (2 * t)) / (zMax - zMin) - t : 0 + out[name] = [ny * scale, nz * scale] + } + return out +} + +export async function applyLayout(req: LayoutRequest): Promise { + const res = await api.layout(req) + const positions = normalizeLayoutPositions(res.positions) + history.execute(new ApplyLayoutCommand(positions, res.clusters)) +} + +export async function applyTopology(req: TopologyRequest): Promise { + // scales are already mapped into the target range server-side (SPEC §6). + const res = await api.topology(req) + history.execute(new ApplyTopologyCommand(res.scales)) +} From e5fee86e7f98f617ceb4e62bcda23335c8cb1961 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 08:35:16 +0100 Subject: [PATCH 016/166] frontend: Three.js render loop + main entry (Phase 12 partial) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stand up the render spine so the app draws its first pixels. Verified live with playwright-cli: /api/config loads, the WebGL canvas mounts, and the tilted coordinate axes render. - src/actions/screen.ts: port of screen.js — renderer, camera (orthographic), window bounds, raycaster, FPS-limited animate() loop. Shiny sync calls dropped. (screen.js was deferred out of Phase 11 for exactly this dependency.) - src/main.ts: port of on_page_load.js — config fetch -> renderer/ camera/scene setup -> mount canvas into #app -> animate. Exposes window.__arena = { ctx, history } so Playwright can read 3D scene state via page.evaluate (WebGL is opaque to the a11y tree). - src/event_listeners.ts: window resize + Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y undo/redo wired to CommandHistory. - src/utils.ts: port of general.js (pure helpers), 6 Vitest tests. - runtime.ts: add ctx.renderer/camera/fps + screen bounds (persist across network reloads; not reset by resetContext). Deleted www/js/general.js (fully ported + tested). Deferred: the canvas mouse/keyboard scene controls (clickDown/Drag/Up, sceneZoom, keyPressed, right-click) land with canvas_controls (still in the Phase 11 list). Documented in PLAN.md. Updated PLAN.md Phase 13 to drive/verify DOM panels with playwright-cli (@playwright/cli, installed) against the window.__arena hook. tsc + eslint clean; 41/41 vitest pass. Co-Authored-By: Claude Fable 5 --- .gitignore | 1 + MIGRATION.md | 4 +- PLAN.md | 19 ++++++-- frontend/src/actions/screen.ts | 82 +++++++++++++++++++++++++++++++++ frontend/src/event_listeners.ts | 26 +++++++++++ frontend/src/main.ts | 47 ++++++++++++++++--- frontend/src/three/runtime.ts | 28 ++++++++++- frontend/src/utils.test.ts | 44 ++++++++++++++++++ frontend/src/utils.ts | 40 ++++++++++++++++ www/js/general.js | 41 ----------------- 10 files changed, 275 insertions(+), 57 deletions(-) create mode 100644 frontend/src/actions/screen.ts create mode 100644 frontend/src/event_listeners.ts create mode 100644 frontend/src/utils.test.ts create mode 100644 frontend/src/utils.ts delete mode 100644 www/js/general.js diff --git a/.gitignore b/.gitignore index 02f279b..af9b419 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ frontend/dist/ # Editors .DS_Store +.playwright-cli/ diff --git a/MIGRATION.md b/MIGRATION.md index 9b8f575..ab5c5d3 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -62,13 +62,13 @@ See `PLAN.md` for the ordered implementation steps. | `www/js/object_actions/layer.js` | `frontend/src/actions/layer.ts` | [ ] | | `www/js/object_actions/node.js` | `frontend/src/actions/node.ts` | [ ] | | `www/js/object_actions/edge.js` | `frontend/src/actions/edge.ts` | [ ] | -| `www/js/object_actions/screen.js` | `frontend/src/actions/screen.ts` | [ ] | +| `www/js/object_actions/screen.js` | `frontend/src/actions/screen.ts` | [x] | | `www/js/object_actions/themes.js` | `frontend/src/actions/themes.ts` | [ ] | | `www/js/object_actions/labels.js` | `frontend/src/actions/labels.ts` | [ ] | | `www/js/object_actions/right_click_menu.js` | `frontend/src/actions/right_click_menu.ts` | [ ] | | `www/js/rshiny_handlers.js` | `frontend/src/bus/index.ts` + `frontend/src/api/client.ts` | [ ] | | `www/js/rshiny_update.js` | `frontend/src/api/client.ts` | [ ] | -| `www/js/general.js` | `frontend/src/utils.ts` | [ ] | +| `www/js/general.js` | `frontend/src/utils.ts` | [x] | | `www/js/event_listeners.js` | `frontend/src/event_listeners.ts` | [ ] | | `www/js/on_page_load.js` | `frontend/src/main.ts` | [ ] | | `www/arena3dweb.css` | Bootstrap 5 + `frontend/src/style.css` | [ ] | diff --git a/PLAN.md b/PLAN.md index 9a025a6..fcd46e9 100644 --- a/PLAN.md +++ b/PLAN.md @@ -161,11 +161,15 @@ Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2 ## Phase 12 — Frontend: Event Handling & Main Loop -- [ ] Migrate `general.js` → `src/utils.ts` -- [ ] Migrate `event_listeners.js` → `src/event_listeners.ts` — add `Ctrl+Z` / `Ctrl+Shift+Z` undo/redo -- [ ] Migrate `on_page_load.js` → `src/main.ts` — Three.js canvas setup, calls `GET /api/config` on load -- [ ] Replace `rshiny_handlers.js` + `rshiny_update.js` with typed EventBus + API client calls -- [ ] Delete `www/js/event_listeners.js`, `www/js/on_page_load.js`, `www/js/general.js` +Render spine done and **verified live** (playwright-cli: config loads from `/api/config`, canvas mounts, tilted coord axes render). Also ported `screen.js` here (renderer/camera/bounds/raycaster/`animate`) — it was deferred out of Phase 11 for exactly this reason. + +- [x] Migrate `general.js` → `src/utils.ts` (6 Vitest tests) +- [x] Migrate `screen.js` → `src/actions/screen.ts` — renderer, camera, window bounds, raycaster, FPS-limited `animate()`. Added `ctx.renderer/camera/fps/*Bound*` (persist across network reloads, not in `resetContext`). +- [x] Migrate `event_listeners.js` (window-level) → `src/event_listeners.ts` — `resize` + `Ctrl+Z` / `Ctrl+Shift+Z` (and `Ctrl+Y`) undo/redo wired to `CommandHistory` +- [x] Migrate `on_page_load.js` → `src/main.ts` — config fetch → renderer/camera/scene setup → mount canvas → animate; exposes `window.__arena = { ctx, history }` test hook (Phase 13 Playwright reads scene state through it) +- [ ] **Deferred (need canvas_controls, still in Phase 11 list):** the canvas mouse/keyboard scene controls (`clickDown/Drag/Up`, `sceneZoom`, `keyPressed`, right-click menu) — land with `canvas_controls.ts`. +- [ ] Replace `rshiny_handlers.js` + `rshiny_update.js` fully with EventBus + API (foundation done: bus/store/api client + commands already dispatch events; remaining handlers map to the Phase 11-deferred actions + Phase 13 panels) +- [ ] Delete `www/js/event_listeners.js`, `www/js/on_page_load.js`; `www/js/general.js` → **can delete now** (fully ported to `src/utils.ts`) - [ ] Delete `www/js/rshiny_handlers.js`, `www/js/rshiny_update.js` - [ ] Delete `www/js/config/` @@ -173,6 +177,11 @@ Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2 ## Phase 13 — Frontend: UI Panels +**Verification with Playwright Agent CLI** (`playwright-cli`, `@playwright/cli@0.1.15`, installed globally). The panels are plain DOM (Bootstrap), so agent-cli's accessibility-ref model drives them deterministically and token-efficiently — use it to build + verify each panel against the running dev server (`npm run dev`), not by eyeballing screenshots: +- Drive each panel: `playwright-cli navigate http://localhost:5173`, then ref-based `click` / `type` / `upload` to exercise buttons, dropdowns, the File upload, and the Undo/Redo buttons. +- **WebGL caveat:** the 3D canvas is opaque to the a11y tree. Assert scene results (node counts/positions, applied layout/cluster/scale) via the `window.__arena = { ctx, history }` hook exposed in Phase 12, read with `playwright-cli`'s JS-eval, rather than pixel diffs. +- As panels stabilise, capture the driven flows as durable `@playwright/test` specs for Phase 14 (agent-cli drives/authoring; `@playwright/test` is the committed artifact). + - [ ] Build `frontend/index.html` — Bootstrap 5 navbar structure matching current tab layout - [ ] Migrate Home panel (`views/home.R` → `src/ui/home.ts`) - [ ] Migrate File panel (`views/file.R` → `src/ui/file.ts`) diff --git a/frontend/src/actions/screen.ts b/frontend/src/actions/screen.ts new file mode 100644 index 0000000..41412fc --- /dev/null +++ b/frontend/src/actions/screen.ts @@ -0,0 +1,82 @@ +// Renderer, camera, window bounds, raycaster and the render loop. +// Port of v2 www/js/object_actions/screen.js. Shiny sync calls dropped. + +import * as THREE from 'three' +import { ctx } from '../three' + +// v2 global raycaster (config/global_variables.js). Reused each frame. +const RAYCASTER = new THREE.Raycaster() +const RAYVECTOR = new THREE.Vector3() +const RAYDIR = new THREE.Vector3() + +export function setRenderer(): void { + ctx.renderer = new THREE.WebGLRenderer({ antialias: true }) +} + +export function resetScreen(): void { + setWindowBounds() + setCamera() + resizeRenderer() +} + +export function setWindowBounds(): void { + ctx.xBoundMin = -window.innerWidth / 2 + ctx.xBoundMax = window.innerWidth / 2 + ctx.yBoundMin = -window.innerHeight / 2 + ctx.yBoundMax = window.innerHeight / 2 + ctx.zBoundMin = -window.innerHeight / 2.5 + ctx.zBoundMax = window.innerHeight / 2.5 +} + +export function setCamera(): void { + // orthographic: left, right, top, bottom, near, far + ctx.camera = new THREE.OrthographicCamera( + ctx.xBoundMin, + ctx.xBoundMax, + ctx.yBoundMax, + ctx.yBoundMin, + -4 * ctx.xBoundMax, + 4 * ctx.xBoundMax + ) + ctx.camera.position.set(0, 0, 100) + ctx.camera.lookAt(0, 0, 0) +} + +export function resizeRenderer(): void { + ctx.renderer?.setSize(2 * ctx.xBoundMax, 2 * ctx.yBoundMax) +} + +export function setRendererColor(hexColor: string): void { + if (ctx.scene?.exists()) ctx.renderer?.setClearColor(hexColor) +} + +// Build the raycaster from a mouse event (used by node/layer hover — Phase 12+). +export function setRaycaster(event: { + clientX: number + clientY: number +}): void { + if (!ctx.camera) return + RAYVECTOR.set( + (event.clientX / window.innerWidth) * 2 - 1, + -(event.clientY / window.innerHeight) * 2 + 1, + -1 // z = -1 important! + ) + RAYVECTOR.unproject(ctx.camera) + RAYDIR.set(0, 0, -1).transformDirection(ctx.camera.matrixWorld) + RAYCASTER.set(RAYVECTOR, RAYDIR) +} + +export function raycaster(): THREE.Raycaster { + return RAYCASTER +} + +// FPS-limited render loop. +export function animate(): void { + setTimeout(() => requestAnimationFrame(animate), 1000 / ctx.fps) + + // ponytail: inter-layer-edge + CSS2D label rendering hook in here once those + // actions land (v2 called renderInterLayerEdges / renderLayerLabels / + // renderNodeLabels before render). Empty scene renders fine without them. + if (ctx.scene && ctx.camera) + ctx.renderer?.render(ctx.scene.THREE_Object, ctx.camera) +} diff --git a/frontend/src/event_listeners.ts b/frontend/src/event_listeners.ts new file mode 100644 index 0000000..e3db752 --- /dev/null +++ b/frontend/src/event_listeners.ts @@ -0,0 +1,26 @@ +// Global event listeners. Port of v2 www/js/event_listeners.js (window-level +// bits) plus new Ctrl+Z / Ctrl+Shift+Z undo/redo. +// +// ponytail: the canvas mouse/keyboard scene controls (clickDown/Drag/Up, +// sceneZoom, keyPressed, right-click menu) live in canvas_controls — deferred +// until that action lands. Only the window-level listeners are wired here. + +import { history } from './commands/base' +import { resetScreen } from './actions/screen' + +export function registerGlobalListeners(): void { + window.addEventListener('resize', resetScreen) + window.addEventListener('keydown', handleUndoRedo) +} + +function handleUndoRedo(e: KeyboardEvent): void { + if (!(e.ctrlKey || e.metaKey)) return + const key = e.key.toLowerCase() + if (key === 'z' && !e.shiftKey) { + e.preventDefault() + history.undo() + } else if ((key === 'z' && e.shiftKey) || key === 'y') { + e.preventDefault() + history.redo() + } +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 84fe971..0c6886d 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,11 +1,44 @@ -// Entry point — Three.js canvas setup and app initialisation -// Ports: www/js/on_page_load.js (Phase 12) +// Entry point — port of v2 www/js/on_page_load.js. +// Fetches config, sets up the Three.js renderer/camera/scene, mounts the +// canvas, wires global listeners and starts the render loop. + import { fetchConfig } from './api/config' +import { store } from './store' +import { ctx, Scene } from './three' +import { history } from './commands/base' +import { setRenderer, resetScreen, animate } from './actions/screen' +import { registerGlobalListeners } from './event_listeners' -fetchConfig() - .then((config) => { - console.log('[arena3d] config loaded', config) - }) - .catch((err: unknown) => { +async function main(): Promise { + const config = await fetchConfig().catch((err: unknown) => { console.error('[arena3d] config fetch failed', err) + return null }) + if (config) { + store.update({ config }) + ctx.edgeDefaultColor = config.edge_default_color + } + + setRenderer() + resetScreen() + + ctx.scene = new Scene() + ctx.scene.tiltDefault() + ctx.scene.setScale(0.9) + + const app = document.getElementById('app') + if (app && ctx.renderer) { + ctx.renderer.setClearColor('#000000') // dark default until a theme is applied + ctx.renderer.domElement.style.display = 'block' + app.appendChild(ctx.renderer.domElement) + } + + registerGlobalListeners() + animate() + + // Test hook: lets Playwright read scene state via page.evaluate (WebGL is + // opaque to the a11y tree). See PLAN Phase 13. + ;(window as unknown as { __arena: unknown }).__arena = { ctx, history } +} + +void main() diff --git a/frontend/src/three/runtime.ts b/frontend/src/three/runtime.ts index c520d65..be1b1f8 100644 --- a/frontend/src/three/runtime.ts +++ b/frontend/src/three/runtime.ts @@ -11,6 +11,7 @@ // here as records accordingly. import type { Scene } from './Scene' +import type * as THREE from 'three' import type { Layer } from './Layer' import type { Node } from './Node' import type { Edge } from './Edge' @@ -18,8 +19,19 @@ import type { Edge } from './Edge' export type ColorPrioritySource = 'default' | 'picker' | 'cluster' export interface RuntimeContext { - // Screen bounds (v2: yBoundMax = window.innerHeight / 2) + // Renderer + camera (Phase 12; persist across network reloads, so NOT reset + // by resetContext). null until main.ts sets up the screen. + renderer: THREE.WebGLRenderer | null + camera: THREE.OrthographicCamera | null + fps: number + + // Screen bounds (v2 screen.js; yBoundMax = window.innerHeight / 2) + xBoundMin: number + xBoundMax: number + yBoundMin: number yBoundMax: number + zBoundMin: number + zBoundMax: number mousePreviousX: number mousePreviousY: number @@ -54,8 +66,20 @@ export interface RuntimeContext { nodeColorPrioritySource: ColorPrioritySource } +const winW = typeof window !== 'undefined' ? window.innerWidth : 800 +const winH = typeof window !== 'undefined' ? window.innerHeight : 800 + export const ctx: RuntimeContext = { - yBoundMax: typeof window !== 'undefined' ? window.innerHeight / 2 : 400, + renderer: null, + camera: null, + fps: 30, + + xBoundMin: -winW / 2, + xBoundMax: winW / 2, + yBoundMin: -winH / 2, + yBoundMax: winH / 2, + zBoundMin: -winH / 2.5, + zBoundMax: winH / 2.5, mousePreviousX: 0, mousePreviousY: 0, diff --git a/frontend/src/utils.test.ts b/frontend/src/utils.test.ts new file mode 100644 index 0000000..21db7b8 --- /dev/null +++ b/frontend/src/utils.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest' +import { + exists, + getUniqueValues, + getCaseInsensitiveIndices, + findIndexByUuid, + getRandomArbitrary, + toRadians, +} from './utils' + +describe('utils', () => { + it('exists uses loose equality', () => { + expect(exists([1, 2, 3], 2)).toBe(true) + expect(exists(['a'], 'b')).toBe(false) + }) + + it('getUniqueValues dedupes preserving order', () => { + expect(getUniqueValues(['a', 'b', 'a', 'c'])).toEqual(['a', 'b', 'c']) + }) + + it('getCaseInsensitiveIndices finds all case-insensitive matches', () => { + expect(getCaseInsensitiveIndices(['Foo', 'bar', 'FOO'], 'foo')).toEqual([ + 0, 2, + ]) + }) + + it('findIndexByUuid returns position or -1', () => { + const arr = [{ uuid: 'x' }, { uuid: 'y' }] + expect(findIndexByUuid(arr, 'y')).toBe(1) + expect(findIndexByUuid(arr, 'z')).toBe(-1) + }) + + it('getRandomArbitrary stays within [min, max)', () => { + for (let i = 0; i < 100; i++) { + const r = getRandomArbitrary(5, 10) + expect(r).toBeGreaterThanOrEqual(5) + expect(r).toBeLessThan(10) + } + }) + + it('toRadians converts degrees', () => { + expect(toRadians(180)).toBeCloseTo(Math.PI) + }) +}) diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts new file mode 100644 index 0000000..2a73475 --- /dev/null +++ b/frontend/src/utils.ts @@ -0,0 +1,40 @@ +// General utilities — port of v2 www/js/general.js. Pure, framework-free. + +export function exists(array: T[], element: T): boolean { + return array.some((l) => l == element) +} + +export function getUniqueValues(array: T[]): T[] { + return [...new Set(array)] +} + +export function getCaseInsensitiveIndices( + array: string[], + element: string +): number[] { + const target = element.toLowerCase() + const indexes: number[] = [] + for (let i = 0; i < array.length; i++) + if (array[i].toLowerCase() === target) indexes.push(i) + return indexes +} + +// Position of a Three.js object's uuid in an array (-1 if absent). +export function findIndexByUuid( + array: { uuid: string }[], + uuid: string +): number { + return array.findIndex((object) => object.uuid === uuid) +} + +// Random float in [min, max). +export function getRandomArbitrary(min: number, max: number): number { + return Math.random() * (max - min) + min +} + +export function toRadians(angle: number): number { + return angle * (Math.PI / 180) +} + +export const delay_ms = (ms: number): Promise => + new Promise((res) => setTimeout(res, ms)) diff --git a/www/js/general.js b/www/js/general.js deleted file mode 100644 index 57f0fc3..0000000 --- a/www/js/general.js +++ /dev/null @@ -1,41 +0,0 @@ -const exists = (array, element) => { - return(array.some(l => l == element)) -}; - -const getUniqueValues = (array) => { - return([...new Set(array)]) -}; - -const getCaseInsensitiveIndices = (array, element) => { - let indexes = []; - element = element.toLowerCase(); //case insensitive - for (let i = 0; i < array.length; i++) - if (array[i].toLowerCase() === element) - indexes.push(i); - - return(indexes) -}; - -// @param array (object): object Array -// @param uuid (string): unique object id -// @return index (int): returned position of uuid in object array -const findIndexByUuid = (array, uuid) => { - const index = array.findIndex(object => { - return(object.uuid === uuid) - }); - - return(index) -}; - -// random float between two values -const getRandomArbitrary = (min, max) => { - return(Math.random() * (max - min) + min) -}; - -const toRadians = (angle) => { - return(angle * (Math.PI / 180)) -}; - -// needed to correctly recenter network for VR -// and for zoom in/out inter edge rendering -const delay_ms = ms => new Promise(res => setTimeout(res, ms)); From de76bbafba6ce5c2e9f7a9c7b47ca4d609a21ce7 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 08:46:00 +0100 Subject: [PATCH 017/166] serena switched to python + ts --- .serena/project.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.serena/project.yml b/.serena/project.yml index 5f746ff..acca853 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -31,8 +31,12 @@ project_name: "Arena3Dweb" # When using multiple languages, the first language server that supports a given file will be used for that file. # The first language is the default language and the respective language server will be used as a fallback. # Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +# R dropped: legacy code being deleted through the v3 migration, and its LSP +# (R `languageserver` package) isn't installed — its failure aborted the whole +# language-server manager. Active dev is TS (frontend) + Python (backend). languages: -- r +- typescript +- python # the encoding used by text files in the project # For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings From b164d4942dde2d7535cc15c7155375eaaf35d97f Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 08:56:58 +0100 Subject: [PATCH 018/166] frontend: network build action feeding LoadNetworkCommand (Phase 12 partial) buildNetwork consumes backend-parsed NetworkData: fresh Scene + ctx registries, layers spread on x, nodes scrambled per layer, edges grouped per src---trg pair with channel colors. loadNetwork wraps it in LoadNetworkCommand so an upload is one undo step. Co-Authored-By: Claude Fable 5 --- PLAN.md | 2 +- frontend/src/actions/network.test.ts | 100 +++++++++++++++++++ frontend/src/actions/network.ts | 144 +++++++++++++++++++++++++++ 3 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 frontend/src/actions/network.test.ts create mode 100644 frontend/src/actions/network.ts diff --git a/PLAN.md b/PLAN.md index fcd46e9..673ae40 100644 --- a/PLAN.md +++ b/PLAN.md @@ -147,7 +147,7 @@ Done now (portable core): Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2D) and Phase 13 (UI/DOM), then delete `www/js/object_actions/`: - [ ] `screen.js` → `src/actions/screen.ts` (renderer, camera, window bounds, raycaster, `animate()`) -- [ ] `network.js` → `src/actions/network.ts` (buildNetwork orchestrator; feeds `LoadNetworkCommand`) +- [x] `network.js` → `src/actions/network.ts` — `buildNetwork` consumes backend `NetworkData` (layer/edge limits enforced server-side; channel limit checked client-side); `loadNetwork` wraps it in `LoadNetworkCommand`. Builds a fresh `Scene` + registries (never mutates in place) so undo snapshots stay valid. Includes interim `initialSpreadLayers`/`scrambleNodes` — move to `layer.ts`/`node.ts` when those port. 4 Vitest tests. *(DragControls/labels/channel-UI/importNetwork stay with their own actions.)* - [ ] `node.js` → `src/actions/node.ts` (build + raycaster hover/selection; color/size actions) - [ ] `edge.js` → `src/actions/edge.ts` (build + inter/intra render toggles) - [ ] `layer.js` → `src/actions/layer.ts` (spread/move + DragControls + layer checkboxes) diff --git a/frontend/src/actions/network.test.ts b/frontend/src/actions/network.test.ts new file mode 100644 index 0000000..8a6c8a3 --- /dev/null +++ b/frontend/src/actions/network.test.ts @@ -0,0 +1,100 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import type { AppConfig } from '../api/config' +import type { EdgeModel, NetworkData } from '../api/client' +import { history } from '../commands/base' +import { store } from '../store' +import { ctx, resetContext } from '../three' +import { buildNetwork, loadNetwork } from './network' + +function edge(partial: Partial): EdgeModel { + return { + src: 'A_L1', + trg: 'B_L2', + source_node: 'A', + source_layer: 'L1', + target_node: 'B', + target_layer: 'L2', + weight: 1, + scaled_weight: 1, + channel: null, + ...partial, + } +} + +function network(partial: Partial = {}): NetworkData { + return { + layers: ['L1', 'L2'], + channels: [], + warnings: [], + nodes: [ + { id: 'A_L1', label: 'A', layer: 'L1' }, + { id: 'B_L2', label: 'B', layer: 'L2' }, + { id: 'C_L1', label: 'C', layer: 'L1' }, + ], + edges: [ + edge({}), + edge({ trg: 'C_L1', target_node: 'C', target_layer: 'L1' }), + ], + ...partial, + } +} + +beforeEach(() => { + resetContext() + store.update({ + config: { + max_channels: 2, + channel_colors_light: ['#111111', '#222222'], + } as AppConfig, + }) +}) + +describe('buildNetwork', () => { + it('builds layers, nodes and edges into ctx', () => { + buildNetwork(network()) + expect(ctx.layers.map((l) => l.name)).toEqual(['L1', 'L2']) + expect(ctx.nodeObjects).toHaveLength(3) + expect(ctx.nodeLayerNames).toEqual(['A_L1', 'B_L2', 'C_L1']) + expect(ctx.nodeGroups['C_L1']).toBe('L1') + expect(ctx.edgeObjects).toHaveLength(2) + expect(ctx.edgeObjects[0].interLayer).toBe(true) + expect(ctx.edgeObjects[1].interLayer).toBe(false) + // layers spread apart on x, nodes scrambled onto their layer + expect(ctx.layers[0].getPosition('x')).not.toBe( + ctx.layers[1].getPosition('x') + ) + }) + + it('collapses same-pair channel rows into one Edge with channel colors', () => { + buildNetwork( + network({ + channels: ['ch1', 'ch2'], + edges: [edge({ channel: 'ch1' }), edge({ channel: 'ch2' })], + }) + ) + expect(ctx.edgeObjects).toHaveLength(1) + expect(ctx.edgeObjects[0].channels).toEqual(['ch1', 'ch2']) + expect(ctx.edgeObjects[0].colors).toEqual(['#111111', '#222222']) + expect(ctx.channelVisibility).toEqual({ ch1: true, ch2: true }) + expect(store.get().selectedChannels).toEqual(['ch1', 'ch2']) + }) +}) + +describe('loadNetwork', () => { + it('is one undoable step; undo restores the previous (empty) registries', () => { + loadNetwork(network()) + expect(ctx.nodeObjects).toHaveLength(3) + expect(history.canUndo).toBe(true) + history.undo() + expect(ctx.nodeObjects).toHaveLength(0) + expect(ctx.layers).toHaveLength(0) + history.redo() + expect(ctx.nodeObjects).toHaveLength(3) + }) + + it('rejects networks above the channel limit', () => { + expect(() => loadNetwork(network({ channels: ['a', 'b', 'c'] }))).toThrow( + /no more than 2 channels/ + ) + }) +}) diff --git a/frontend/src/actions/network.ts b/frontend/src/actions/network.ts new file mode 100644 index 0000000..f1bf853 --- /dev/null +++ b/frontend/src/actions/network.ts @@ -0,0 +1,144 @@ +// Port of v2 www/js/object_actions/network.js — buildNetwork orchestrator. +// Consumes the backend-parsed NetworkData (POST /api/network) instead of raw +// TSV columns; layer/edge-count limits are enforced server-side. +// Deferred with their phases: DragControls + labels + channel UI (12/13), +// importNetwork from session JSON (session action), applyTheme (themes.ts). + +import type { EdgeModel, NetworkData, NodeModel } from '../api/client' +import { history } from '../commands/base' +import { LoadNetworkCommand } from '../commands/scene' +import { store } from '../store' +import { + COLOR_VECTOR_280, + ctx, + Edge, + Layer, + Node, + resetContext, + Scene, +} from '../three' +import { getRandomArbitrary } from '../utils' + +// v2 uploadNetwork(): one undoable step wrapping the full build. +export function loadNetwork(data: NetworkData): void { + const maxChannels = store.get().config?.max_channels + if (maxChannels !== undefined && data.channels.length > maxChannels) + throw new Error( + `Network must contain no more than ${maxChannels} channels.` + ) + history.execute(new LoadNetworkCommand(() => buildNetwork(data))) +} + +export function buildNetwork(data: NetworkData): void { + // Fresh Scene + registries (not mutated in place) so LoadNetworkCommand's + // prev-snapshot still points at the intact previous scene graph. + const edgeDefaultColor = ctx.edgeDefaultColor + resetContext() + ctx.edgeDefaultColor = edgeDefaultColor + + const scene = new Scene() + scene.tiltDefault() + scene.setScale(0.9) + ctx.scene = scene + + initializeLayers(data.layers) + initializeChannels(data.channels) + initializeNodes(data.nodes) + createEdgeObjects(data.edges) + + store.update({ network: data, selectedChannels: [...data.channels] }) +} + +function initializeLayers(names: string[]): void { + names.forEach((name, i) => { + ctx.layerGroups[name] = i + const layer = new Layer({ id: i, name }) + ctx.layers.push(layer) + ctx.scene!.addLayer(layer.plane) + }) + initialSpreadLayers() +} + +// v2 layer.js initialSpreadLayers — lives here until actions/layer.ts is ported. +export function initialSpreadLayers(direction = 1): void { + const n = ctx.layers.length + const spacing = (ctx.xBoundMax * 2) / n + ctx.layers.forEach((layer, i) => { + const offset = + n % 2 + ? (i - Math.floor(n / 2)) * spacing + : (i - n / 2) * spacing + spacing / 2 + layer.translateX(direction * offset) + }) +} + +function initializeChannels(channels: string[]): void { + const palette = store.get().config?.channel_colors_light ?? [] + channels.forEach((channel, i) => { + ctx.channelColors[channel] = + palette[i % palette.length] ?? ctx.edgeDefaultColor + ctx.channelVisibility[channel] = true + }) +} + +function initializeNodes(nodes: NodeModel[]): void { + nodes.forEach((n, i) => { + ctx.nodeLayerNames.push(n.id) + ctx.nodeGroups[n.id] = n.layer + const layerIndex = ctx.layerGroups[n.layer] + const node = new Node({ + id: i, + name: n.label, + layer: n.layer, + nodeLayerName: n.id, + color: COLOR_VECTOR_280[layerIndex % COLOR_VECTOR_280.length], + }) + ctx.nodeObjects.push(node) + ctx.layers[layerIndex].addNode(node.sphere) + }) + scrambleNodes() +} + +// v2 node.js scrambleNodes — lives here until actions/node.ts is ported. +export function scrambleNodes( + yMin = ctx.yBoundMin, + yMax = ctx.yBoundMax, + zMin = ctx.zBoundMin, + zMax = ctx.zBoundMax +): void { + for (const node of ctx.nodeObjects) { + node.translateY(getRandomArbitrary(yMin, yMax)) + node.translateZ(getRandomArbitrary(zMin, zMax)) + } +} + +// v2 edge.js createEdgeObjects: one Edge per src---trg pair; multi-channel +// rows between the same pair collapse into that Edge's channel arrays. +function createEdgeObjects(edges: EdgeModel[]): void { + const byPair = new Map() + for (const e of edges) { + const key = `${e.src}---${e.trg}` + const group = byPair.get(key) + if (group) group.push(e) + else byPair.set(key, [e]) + } + + let id = 0 + for (const group of byPair.values()) { + const first = group[0] + const hasChannels = first.channel !== null + ctx.edgeObjects.push( + new Edge({ + id: id++, + source: first.src, + target: first.trg, + weights: group.map((e) => e.scaled_weight), + channels: hasChannels ? group.map((e) => e.channel!) : [], + colors: hasChannels + ? group.map((e) => ctx.channelColors[e.channel!]) + : undefined, + interLayer: first.source_layer !== first.target_layer, + }) + ) + } +} From 69aca456f14113f22a960f53a511f0d568a49a5a Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 09:49:12 +0100 Subject: [PATCH 019/166] =?UTF-8?q?frontend:=20node=20action=20=E2=80=94?= =?UTF-8?q?=20hover,=20selection,=20repaint=20(Phase=2012=20partial)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port of www/js/object_actions/node.js pure parts: raycaster hover, double-click/search/select-all selection synced to the store, repaintNode (cluster branch folded into Node.getColor), label-flag priorities using Layer.isVisible instead of DOM checkboxes, shape/color-priority setters. scrambleNodes moves here from network.ts. ctx gains lastHoveredNodeIndex, selectedNodeColorFlag and the two node-label flags. DOM-bound pieces (search bar, sliders, lasso, node description div, attribute upload) stay with canvas_controls.ts / Phase 13 UI. Co-Authored-By: Claude Fable 5 --- PLAN.md | 4 +- frontend/src/actions/network.ts | 15 +-- frontend/src/actions/node.test.ts | 110 ++++++++++++++++++++ frontend/src/actions/node.ts | 160 ++++++++++++++++++++++++++++++ frontend/src/three/runtime.ts | 15 +++ 5 files changed, 288 insertions(+), 16 deletions(-) create mode 100644 frontend/src/actions/node.test.ts create mode 100644 frontend/src/actions/node.ts diff --git a/PLAN.md b/PLAN.md index 673ae40..c676fa3 100644 --- a/PLAN.md +++ b/PLAN.md @@ -147,8 +147,8 @@ Done now (portable core): Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2D) and Phase 13 (UI/DOM), then delete `www/js/object_actions/`: - [ ] `screen.js` → `src/actions/screen.ts` (renderer, camera, window bounds, raycaster, `animate()`) -- [x] `network.js` → `src/actions/network.ts` — `buildNetwork` consumes backend `NetworkData` (layer/edge limits enforced server-side; channel limit checked client-side); `loadNetwork` wraps it in `LoadNetworkCommand`. Builds a fresh `Scene` + registries (never mutates in place) so undo snapshots stay valid. Includes interim `initialSpreadLayers`/`scrambleNodes` — move to `layer.ts`/`node.ts` when those port. 4 Vitest tests. *(DragControls/labels/channel-UI/importNetwork stay with their own actions.)* -- [ ] `node.js` → `src/actions/node.ts` (build + raycaster hover/selection; color/size actions) +- [x] `network.js` → `src/actions/network.ts` — `buildNetwork` consumes backend `NetworkData` (layer/edge limits enforced server-side; channel limit checked client-side); `loadNetwork` wraps it in `LoadNetworkCommand`. Builds a fresh `Scene` + registries (never mutates in place) so undo snapshots stay valid. Includes interim `initialSpreadLayers` — moves to `layer.ts` when it ports. 4 Vitest tests. *(DragControls/labels/channel-UI/importNetwork stay with their own actions.)* +- [x] `node.js` → `src/actions/node.ts` — hover (`checkHoverOverNode`), double-click + search + select-all selection (store-synced), `repaintNode` (cluster branch folded into `Node.getColor`), label-flag priorities (DOM checkboxes → `Layer.isVisible`), shape/color-priority setters; `scrambleNodes` moved here from `network.ts`. New ctx state: `lastHoveredNodeIndex`, `selectedNodeColorFlag`, `showAll/showSelectedNodeLabelsFlag`. 7 Vitest tests. *(Search bar/sliders/descr div → Phase 13; lasso + held-key/spread/move/scale → canvas_controls.ts; attribute upload → POST /api/attributes.)* - [ ] `edge.js` → `src/actions/edge.ts` (build + inter/intra render toggles) - [ ] `layer.js` → `src/actions/layer.ts` (spread/move + DragControls + layer checkboxes) - [ ] `themes.js` → `src/actions/themes.ts` (needs renderer `setClearColor`; listens to `theme:changed`) diff --git a/frontend/src/actions/network.ts b/frontend/src/actions/network.ts index f1bf853..25923a8 100644 --- a/frontend/src/actions/network.ts +++ b/frontend/src/actions/network.ts @@ -17,7 +17,7 @@ import { resetContext, Scene, } from '../three' -import { getRandomArbitrary } from '../utils' +import { scrambleNodes } from './node' // v2 uploadNetwork(): one undoable step wrapping the full build. export function loadNetwork(data: NetworkData): void { @@ -99,19 +99,6 @@ function initializeNodes(nodes: NodeModel[]): void { scrambleNodes() } -// v2 node.js scrambleNodes — lives here until actions/node.ts is ported. -export function scrambleNodes( - yMin = ctx.yBoundMin, - yMax = ctx.yBoundMax, - zMin = ctx.zBoundMin, - zMax = ctx.zBoundMax -): void { - for (const node of ctx.nodeObjects) { - node.translateY(getRandomArbitrary(yMin, yMax)) - node.translateZ(getRandomArbitrary(zMin, zMax)) - } -} - // v2 edge.js createEdgeObjects: one Edge per src---trg pair; multi-channel // rows between the same pair collapse into that Edge's channel arrays. function createEdgeObjects(edges: EdgeModel[]): void { diff --git a/frontend/src/actions/node.test.ts b/frontend/src/actions/node.test.ts new file mode 100644 index 0000000..0f5b6b5 --- /dev/null +++ b/frontend/src/actions/node.test.ts @@ -0,0 +1,110 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import { store } from '../store' +import { + ctx, + Layer, + Node, + resetContext, + SELECTED_DEFAULT_COLOR, +} from '../three' +import { + decideNodeLabelFlags, + getSelectedNodes, + repaintNode, + selectAllNodes, + selectNodesByName, + setNodeSelectedColorPriority, + unselectAllNodes, +} from './node' + +function seed(): void { + ctx.layers = [new Layer({ id: 0, name: 'L1' })] + ctx.layerGroups = { L1: 0 } + ctx.nodeGroups = { A_L1: 'L1', B_L1: 'L1' } + ctx.nodeLayerNames = ['A_L1', 'B_L1'] + ctx.nodeObjects = [ + new Node({ + id: 0, + name: 'A', + nodeLayerName: 'A_L1', + layer: 'L1', + color: '#123456', + }), + new Node({ + id: 1, + name: 'B', + nodeLayerName: 'B_L1', + layer: 'L1', + color: '#123456', + }), + ] +} + +beforeEach(() => { + resetContext() + store.update({ selectedNodes: [] }) + seed() +}) + +describe('selection', () => { + it('selectAllNodes selects, repaints and syncs the store; unselect reverts', () => { + selectAllNodes(true) + expect(getSelectedNodes()).toEqual([0, 1]) + expect(ctx.nodeObjects[0].color).toBe(SELECTED_DEFAULT_COLOR) + expect(store.get().selectedNodes).toEqual(['A_L1', 'B_L1']) + + unselectAllNodes() + expect(getSelectedNodes()).toEqual([]) + expect(ctx.nodeObjects[0].color).toBe('#123456') + expect(store.get().selectedNodes).toEqual([]) + }) + + it('selectNodesByName matches comma-separated names case-insensitively', () => { + selectNodesByName(' a , nosuch') + expect(getSelectedNodes()).toEqual([0]) + expect(store.get().selectedNodes).toEqual(['A_L1']) + }) +}) + +describe('repaintNode', () => { + it('honours the selected-color priority flag', () => { + ctx.nodeObjects[0].isSelected = true + setNodeSelectedColorPriority(false) + expect(ctx.nodeObjects[0].color).toBe('#123456') + setNodeSelectedColorPriority(true) + expect(ctx.nodeObjects[0].color).toBe(SELECTED_DEFAULT_COLOR) + }) + + it('falls back to cluster color when cluster priority is active', () => { + ctx.nodeColorPrioritySource = 'cluster' + ctx.nodeObjects[0].setColor('#ff0000', false, true) // clusterMode + ctx.nodeObjects[0].setColor('#00ff00') // transient color + repaintNode(0) + expect(ctx.nodeObjects[0].color).toBe('#ff0000') + }) +}) + +describe('decideNodeLabelFlags', () => { + it('hidden layer wins over everything; selected shows when flag set', () => { + ctx.nodeObjects[0].isSelected = true + decideNodeLabelFlags() // showSelectedNodeLabelsFlag default true + expect(ctx.nodeObjects[0].showLabel).toBe(true) + expect(ctx.nodeObjects[1].showLabel).toBe(false) + + ctx.layers[0].isVisible = false + decideNodeLabelFlags() + expect(ctx.nodeObjects[0].showLabel).toBe(false) + }) + + it('showAllNodeLabelsFlag shows every visible-layer label', () => { + ctx.showAllNodeLabelsFlag = true + decideNodeLabelFlags() + expect(ctx.nodeObjects.every((n) => n.showLabel)).toBe(true) + }) + + it('hovered node label shows', () => { + ctx.lastHoveredNodeIndex = 1 + decideNodeLabelFlags() + expect(ctx.nodeObjects[1].showLabel).toBe(true) + }) +}) diff --git a/frontend/src/actions/node.ts b/frontend/src/actions/node.ts new file mode 100644 index 0000000..3989065 --- /dev/null +++ b/frontend/src/actions/node.ts @@ -0,0 +1,160 @@ +// Port of v2 www/js/object_actions/node.js — hover, selection, repaint, +// shape/color-priority setters. Selection state syncs to the store instead +// of Shiny inputs. Deferred with their phases: search bar / sliders / node +// description div (Phase 13 UI), lasso + held-key node moves + spread/move/ +// scale sliders (canvas_controls.ts), attribute upload (POST /api/attributes). + +import { store } from '../store' +import { ctx, SELECTED_DEFAULT_COLOR } from '../three' +import type { ColorPrioritySource, NodeGeometryType } from '../three' +import { + findIndexByUuid, + getCaseInsensitiveIndices, + getRandomArbitrary, +} from '../utils' +import { raycaster, setRaycaster } from './screen' + +// v2 node.js scrambleNodes — random y/z spread onto each node's layer plane. +export function scrambleNodes( + yMin = ctx.yBoundMin, + yMax = ctx.yBoundMax, + zMin = ctx.zBoundMin, + zMax = ctx.zBoundMax +): void { + for (const node of ctx.nodeObjects) { + node.translateY(getRandomArbitrary(yMin, yMax)) + node.translateZ(getRandomArbitrary(zMin, zMax)) + } +} + +function updateSelectedNodesStore(): void { + store.update({ + selectedNodes: ctx.nodeObjects + .filter((n) => n.isSelected) + .map((n) => n.nodeLayerName), + }) +} + +// @return whether the pointer is over a node +export function checkHoverOverNode(event: { + clientX: number + clientY: number +}): boolean { + setRaycaster(event) + const spheres = ctx.nodeObjects.map(({ sphere }) => sphere) + const intersects = raycaster().intersectObjects(spheres) + let changed = false + let hovering = false + + if (ctx.lastHoveredNodeIndex !== null) { + ctx.nodeObjects[ctx.lastHoveredNodeIndex].setOpacity(1) + ctx.lastHoveredNodeIndex = null + changed = true + } + + if (intersects.length > 0) { + const i = findIndexByUuid(spheres, intersects[0].object.uuid) + ctx.nodeObjects[i].setOpacity(0.5) + ctx.lastHoveredNodeIndex = i + changed = true + hovering = true + } + + if (changed) decideNodeLabelFlags() + return hovering +} + +// v2 read the hideLayer DOM checkboxes; layer visibility now lives on Layer. +export function decideNodeLabelFlags(): void { + ctx.nodeObjects.forEach((node, i) => { + const layer = + ctx.layers[ctx.layerGroups[ctx.nodeGroups[ctx.nodeLayerNames[i]]]] + // Priorities list: + if (!layer.isVisible) node.showLabel = false + else if (ctx.showAllNodeLabelsFlag) node.showLabel = true + else if (layer.showNodeLabels) node.showLabel = true + else if (ctx.showSelectedNodeLabelsFlag && node.isSelected) + node.showLabel = true + else if (i === ctx.lastHoveredNodeIndex) node.showLabel = true + else node.showLabel = false + }) +} + +// @return selected node indexes +export function getSelectedNodes(): number[] { + return ctx.nodeObjects.filter((n) => n.isSelected).map((n) => n.id) +} + +// @return whether a node was toggled +export function performDoubleClickNodeSelection(event: { + clientX: number + clientY: number +}): boolean { + setRaycaster(event) + const spheres = ctx.nodeObjects.map(({ sphere }) => sphere) + const intersects = raycaster().intersectObjects(spheres) + if (intersects.length === 0) return false + + const i = findIndexByUuid(spheres, intersects[0].object.uuid) + ctx.nodeObjects[i].isSelected = !ctx.nodeObjects[i].isSelected + repaintNode(i) + decideNodeLabelFlags() + updateSelectedNodesStore() + return true +} + +export function repaintNode(i: number): void { + const node = ctx.nodeObjects[i] + if (ctx.selectedNodeColorFlag && node.isSelected) + node.setColor(SELECTED_DEFAULT_COLOR) + // getColor resolves the default/picker/cluster priority (v2 repaintNode's + // manual cluster branch is folded into it) + else node.setColor(node.getColor()) +} + +export function repaintNodes(): void { + ctx.nodeObjects.forEach((_, i) => repaintNode(i)) +} + +export function selectAllNodes(selected: boolean): void { + ctx.nodeObjects.forEach((node, i) => { + node.isSelected = selected + repaintNode(i) + }) + decideNodeLabelFlags() + updateSelectedNodesStore() +} + +export function unselectAllNodes(): void { + selectAllNodes(false) +} + +// Core of v2 selectSearchedNodes minus the DOM search bar (Phase 13 wires +// the input + Enter key to this). Comma-separated, case-insensitive names. +export function selectNodesByName(search: string): void { + const names = ctx.nodeObjects.map(({ name }) => name) + for (const term of search.split(',')) { + for (const i of getCaseInsensitiveIndices(names, term.trim())) { + if (!ctx.nodeObjects[i].isSelected) { + ctx.nodeObjects[i].isSelected = true + repaintNode(i) + } + } + } + decideNodeLabelFlags() + updateSelectedNodesStore() +} + +export function setNodeShape(shape: NodeGeometryType): void { + for (const node of ctx.nodeObjects) node.setGeometry(shape) +} + +export function setNodeColorPriority(source: ColorPrioritySource): void { + ctx.nodeColorPrioritySource = source + repaintNodes() +} + +export function setNodeSelectedColorPriority(flag: boolean): void { + ctx.selectedNodeColorFlag = flag + repaintNodes() +} diff --git a/frontend/src/three/runtime.ts b/frontend/src/three/runtime.ts index be1b1f8..3e8e215 100644 --- a/frontend/src/three/runtime.ts +++ b/frontend/src/three/runtime.ts @@ -64,6 +64,12 @@ export interface RuntimeContext { // Color-priority sources layerColorPrioritySource: ColorPrioritySource nodeColorPrioritySource: ColorPrioritySource + + // Node hover/selection/label state (v2 node.js globals) + lastHoveredNodeIndex: number | null + selectedNodeColorFlag: boolean + showAllNodeLabelsFlag: boolean + showSelectedNodeLabelsFlag: boolean } const winW = typeof window !== 'undefined' ? window.innerWidth : 800 @@ -108,6 +114,11 @@ export const ctx: RuntimeContext = { layerColorPrioritySource: 'default', nodeColorPrioritySource: 'default', + + lastHoveredNodeIndex: null, + selectedNodeColorFlag: true, + showAllNodeLabelsFlag: false, + showSelectedNodeLabelsFlag: true, } // Reset registries + tunables to defaults (used by tests and network reload). @@ -136,6 +147,10 @@ export function resetContext(): void { ctx.intraChannelCurvature = 15 ctx.layerColorPrioritySource = 'default' ctx.nodeColorPrioritySource = 'default' + ctx.lastHoveredNodeIndex = null + ctx.selectedNodeColorFlag = true + ctx.showAllNodeLabelsFlag = false + ctx.showSelectedNodeLabelsFlag = true } // Registry snapshot — the mutable scene-graph state a network load replaces. From e2ad38f4834325d4bdf12ffda24d565ee6819d59 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 09:57:27 +0100 Subject: [PATCH 020/166] remove ci tests from this branch --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17ab869..b08f2d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: [main, v3, "v3-*"] + branches: [main, v3] pull_request: jobs: From f3259c6a2e07967b94f7d7da6404e21b8ec35bc4 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 09:57:47 +0100 Subject: [PATCH 021/166] fix frontend lint --- frontend/src/actions/layout.test.ts | 10 ++++------ frontend/src/api/client.ts | 24 +++++++++++++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/frontend/src/actions/layout.test.ts b/frontend/src/actions/layout.test.ts index 784882b..9e968f3 100644 --- a/frontend/src/actions/layout.test.ts +++ b/frontend/src/actions/layout.test.ts @@ -49,12 +49,10 @@ describe('normalizeLayoutPositions', () => { describe('applyLayout', () => { it('fetches, normalizes, and pushes one ApplyLayoutCommand', async () => { - const spy = vi - .spyOn(api, 'layout') - .mockResolvedValue({ - positions: { 'A::L1': [0, 0], 'B::L1': [10, 10] }, - clusters: null, - }) + const spy = vi.spyOn(api, 'layout').mockResolvedValue({ + positions: { 'A::L1': [0, 0], 'B::L1': [10, 10] }, + clusters: null, + }) await applyLayout({ nodes: [], edges: [], diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 72e2690..d7cbc2e 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -73,28 +73,38 @@ async function post(path: string, body: unknown): Promise { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }) - if (!res.ok) throw new Error(`POST ${path} failed: ${res.status} ${await res.text()}`) + if (!res.ok) + throw new Error(`POST ${path} failed: ${res.status} ${await res.text()}`) return res.json() as Promise } -async function postFile(path: string, file: File | Blob, name = 'file'): Promise { +async function postFile( + path: string, + file: File | Blob, + name = 'file' +): Promise { const form = new FormData() form.append('file', file, name) const res = await fetch(path, { method: 'POST', body: form }) - if (!res.ok) throw new Error(`POST ${path} failed: ${res.status} ${await res.text()}`) + if (!res.ok) + throw new Error(`POST ${path} failed: ${res.status} ${await res.text()}`) return res.json() as Promise } export const api = { - uploadNetwork: (file: File | Blob) => postFile('/api/network', file), + uploadNetwork: (file: File | Blob) => + postFile('/api/network', file), layout: (req: LayoutRequest) => post('/api/layout', req), - topology: (req: TopologyRequest) => post('/api/topology', req), - importSession: (file: File | Blob) => postFile>('/api/session/import', file), + topology: (req: TopologyRequest) => + post('/api/topology', req), + importSession: (file: File | Blob) => + postFile>('/api/session/import', file), createExternal: (session: unknown) => post<{ token: string; url: string }>('/api/external', session), resolveExternal: async (token: string): Promise> => { const res = await fetch(`/api/external/${token}`) - if (!res.ok) throw new Error(`GET /api/external/${token} failed: ${res.status}`) + if (!res.ok) + throw new Error(`GET /api/external/${token} failed: ${res.status}`) return res.json() as Promise> }, } From ec9f18e51b9ab02b5232f0942de376eb91a8f352 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 10:03:09 +0100 Subject: [PATCH 022/166] =?UTF-8?q?frontend:=20edge=20action=20=E2=80=94?= =?UTF-8?q?=20inter/intra=20render=20machinery=20+=20setters=20(Phase=2012?= =?UTF-8?q?=20partial)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port of www/js/object_actions/edge.js pure parts: renderInterLayerEdges now runs every animate() tick (remove while dragging/paused/invisible, v2 locked-flag redraw pair), channel color/visibility, unselectAllEdges, and the edge-setting setters (direction, arrow sizes, width-by-weight, opacities, color priorities, curvatures). redrawIntra/InterLayerEdges move here from commands/scene.ts private helpers. ctx gains the four inter-layer render flags. Channel UI lists/picker stay with Phase 13; edge attributes upload with POST /api/attributes; edge build already lives in network.ts. Co-Authored-By: Claude Fable 5 --- PLAN.md | 2 +- frontend/src/actions/edge.test.ts | 108 ++++++++++++++++++++++ frontend/src/actions/edge.ts | 143 ++++++++++++++++++++++++++++++ frontend/src/actions/screen.ts | 7 +- frontend/src/commands/scene.ts | 9 +- frontend/src/three/runtime.ts | 15 ++++ 6 files changed, 272 insertions(+), 12 deletions(-) create mode 100644 frontend/src/actions/edge.test.ts create mode 100644 frontend/src/actions/edge.ts diff --git a/PLAN.md b/PLAN.md index c676fa3..7e3a89b 100644 --- a/PLAN.md +++ b/PLAN.md @@ -149,7 +149,7 @@ Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2 - [ ] `screen.js` → `src/actions/screen.ts` (renderer, camera, window bounds, raycaster, `animate()`) - [x] `network.js` → `src/actions/network.ts` — `buildNetwork` consumes backend `NetworkData` (layer/edge limits enforced server-side; channel limit checked client-side); `loadNetwork` wraps it in `LoadNetworkCommand`. Builds a fresh `Scene` + registries (never mutates in place) so undo snapshots stay valid. Includes interim `initialSpreadLayers` — moves to `layer.ts` when it ports. 4 Vitest tests. *(DragControls/labels/channel-UI/importNetwork stay with their own actions.)* - [x] `node.js` → `src/actions/node.ts` — hover (`checkHoverOverNode`), double-click + search + select-all selection (store-synced), `repaintNode` (cluster branch folded into `Node.getColor`), label-flag priorities (DOM checkboxes → `Layer.isVisible`), shape/color-priority setters; `scrambleNodes` moved here from `network.ts`. New ctx state: `lastHoveredNodeIndex`, `selectedNodeColorFlag`, `showAll/showSelectedNodeLabelsFlag`. 7 Vitest tests. *(Search bar/sliders/descr div → Phase 13; lasso + held-key/spread/move/scale → canvas_controls.ts; attribute upload → POST /api/attributes.)* -- [ ] `edge.js` → `src/actions/edge.ts` (build + inter/intra render toggles) +- [x] `edge.js` → `src/actions/edge.ts` — inter-layer render machinery (`renderInterLayerEdges` wired into `animate()`, remove-on-drag/pause/zero-opacity, v2 locked-flag redraw pair), `redrawIntra/InterLayerEdges` (moved out of `commands/scene.ts` privates), channel color/visibility, `unselectAllEdges`, all edge-setting setters. New ctx state: `renderInterLayerEdgesFlag`, `waitEdgeRenderFlag`, `interEdgesRemoved`, `interLayerEdgesRenderPauseFlag`. 5 Vitest tests. *(Edge build lives in network.ts; channel UI lists/picker → Phase 13; attributes upload → POST /api/attributes.)* - [ ] `layer.js` → `src/actions/layer.ts` (spread/move + DragControls + layer checkboxes) - [ ] `themes.js` → `src/actions/themes.ts` (needs renderer `setClearColor`; listens to `theme:changed`) - [ ] `labels.js` → `src/actions/labels.ts` (CSS2D label rendering in the animate loop) diff --git a/frontend/src/actions/edge.test.ts b/frontend/src/actions/edge.test.ts new file mode 100644 index 0000000..9e53ef5 --- /dev/null +++ b/frontend/src/actions/edge.test.ts @@ -0,0 +1,108 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import { ctx, Edge, Layer, Node, resetContext, Scene } from '../three' +import { + renderInterLayerEdges, + setChannelColor, + setChannelVisibility, + setEdgeWidthByWeight, + setInterLayerEdgeOpacity, + toggleInterLayerEdgesRendering, +} from './edge' + +// Two layers, one node each, one inter-layer edge. +function seed(): void { + ctx.scene = new Scene() + ctx.layers = [ + new Layer({ id: 0, name: 'L1' }), + new Layer({ id: 1, name: 'L2' }), + ] + ctx.layerGroups = { L1: 0, L2: 1 } + ctx.nodeGroups = { A_L1: 'L1', B_L2: 'L2' } + ctx.nodeLayerNames = ['A_L1', 'B_L2'] + ctx.nodeObjects = [ + new Node({ id: 0, name: 'A', nodeLayerName: 'A_L1', layer: 'L1' }), + new Node({ id: 1, name: 'B', nodeLayerName: 'B_L2', layer: 'L2' }), + ] + ctx.layers[0].addNode(ctx.nodeObjects[0].sphere) + ctx.layers[1].addNode(ctx.nodeObjects[1].sphere) + ctx.edgeObjects = [ + new Edge({ + id: 0, + source: 'A_L1', + target: 'B_L2', + weights: [1], + interLayer: true, + }), + ] +} + +beforeEach(() => { + resetContext() + seed() +}) + +describe('renderInterLayerEdges', () => { + it('removes inter-layer edges while paused, redraws after unpausing', () => { + const obj = ctx.edgeObjects[0].THREE_Object + expect(ctx.scene!.exists()).toBe(true) + + toggleInterLayerEdgesRendering() // pause + renderInterLayerEdges() + expect(ctx.interEdgesRemoved).toBe(true) + expect(ctx.scene!.THREE_Object.children).not.toContain(obj) + + toggleInterLayerEdgesRendering() // unpause raises the render flag + expect(ctx.renderInterLayerEdgesFlag).toBe(true) + renderInterLayerEdges() + expect(ctx.interEdgesRemoved).toBe(false) + expect(ctx.scene!.THREE_Object.children).toContain( + ctx.edgeObjects[0].THREE_Object + ) + }) + + it('redraws for one extra tick, then clears the flag (locked-flag pair)', () => { + ctx.renderInterLayerEdgesFlag = true + renderInterLayerEdges() // tick 1: waitEdgeRenderFlag drops + expect(ctx.renderInterLayerEdgesFlag).toBe(true) + renderInterLayerEdges() // tick 2: flag clears, re-arms + expect(ctx.renderInterLayerEdgesFlag).toBe(false) + expect(ctx.waitEdgeRenderFlag).toBe(true) + }) + + it('removes edges when opacity is 0 and width-by-weight is off', () => { + setEdgeWidthByWeight(false) + setInterLayerEdgeOpacity(0) + renderInterLayerEdges() + expect(ctx.interEdgesRemoved).toBe(true) + }) +}) + +describe('channels', () => { + it('setChannelColor updates the palette and raises the render flag', () => { + ctx.renderInterLayerEdgesFlag = false + setChannelColor('ch1', '#abcdef') + expect(ctx.channelColors['ch1']).toBe('#abcdef') + expect(ctx.renderInterLayerEdgesFlag).toBe(true) + }) + + it('setChannelVisibility toggles the tagged channel line', () => { + ctx.channelColors = { ch1: '#ff0000' } + ctx.edgeObjects = [ + new Edge({ + id: 0, + source: 'A_L1', + target: 'B_L2', + weights: [1], + channels: ['ch1'], + colors: ['#ff0000'], + interLayer: true, + }), + ] + setChannelVisibility('ch1', false) + const line = ctx.edgeObjects[0].THREE_Object.children.find( + (c) => c.userData.tag === 'ch1' + ) + expect(line?.visible).toBe(false) + expect(ctx.channelVisibility['ch1']).toBe(false) + }) +}) diff --git a/frontend/src/actions/edge.ts b/frontend/src/actions/edge.ts new file mode 100644 index 0000000..c4856b2 --- /dev/null +++ b/frontend/src/actions/edge.ts @@ -0,0 +1,143 @@ +// Port of v2 www/js/object_actions/edge.js — inter/intra-layer edge render +// machinery, channel color/visibility, and edge render settings. +// createEdgeObjects/assignChannelColorsFromPalette live in network.ts's +// build. Deferred with their phases: channel layout/edit lists + color-picker +// DOM (Phase 13 UI), edge attributes upload (POST /api/attributes), +// pause-button label (canvas_controls.ts). + +import { ctx } from '../three' + +export function redrawIntraLayerEdges(): void { + for (const e of ctx.edgeObjects) if (!e.interLayer) e.redrawEdge() +} + +export function redrawInterLayerEdges(): void { + for (const e of ctx.edgeObjects) if (e.interLayer) e.redrawEdge() + + ctx.interEdgesRemoved = false + // v2's locked-flag pair: redraw runs for one extra animate tick after the + // flag was raised, then re-arms + if (ctx.waitEdgeRenderFlag) { + ctx.waitEdgeRenderFlag = false + } else { + ctx.renderInterLayerEdgesFlag = false + ctx.waitEdgeRenderFlag = true + } +} + +// Called every animate() tick. +export function renderInterLayerEdges(): void { + if (existsConditionToRemoveInterEdges()) { + if (!ctx.interEdgesRemoved) removeInterLayerEdges() + } else if (ctx.renderInterLayerEdgesFlag) { + redrawInterLayerEdges() + } +} + +function existsConditionToRemoveInterEdges(): boolean { + return ( + (ctx.scene?.dragging ?? false) || + ctx.interLayerEdgesRenderPauseFlag || + (!ctx.edgeWidthByWeight && ctx.interLayerEdgeOpacity === 0) + ) +} + +function removeInterLayerEdges(): void { + for (const e of ctx.edgeObjects) + if (e.interLayer) ctx.scene?.remove(e.THREE_Object) + ctx.interEdgesRemoved = true +} + +// @return whether rendering is now paused (v2 toggleInterLayerEdgesRendering; +// the pause-button label is Phase 13's job) +export function toggleInterLayerEdgesRendering(): boolean { + ctx.interLayerEdgesRenderPauseFlag = !ctx.interLayerEdgesRenderPauseFlag + if (!ctx.interLayerEdgesRenderPauseFlag) ctx.renderInterLayerEdgesFlag = true + return ctx.interLayerEdgesRenderPauseFlag +} + +// v2 changeChannelColor minus the DOM picker element. +export function setChannelColor(channel: string, color: string): void { + ctx.channelColors[channel] = color + ctx.renderInterLayerEdgesFlag = true + redrawIntraLayerEdges() +} + +// v2 toggleChannelVisibility minus the DOM checkbox (checked = hidden there; +// this takes `visible` directly). +export function setChannelVisibility(channel: string, visible: boolean): void { + for (const edge of ctx.edgeObjects) { + const children = edge.THREE_Object.children + for (let j = 0; j < children.length; j++) { + if (children[j].userData.tag === channel) { + children[j].visible = visible + if (ctx.isDirectionEnabled) children[j + 1].visible = visible // arrow + ctx.channelVisibility[channel] = visible + break // only one channel allowed per edge + } + } + } +} + +export function unselectAllEdges(): void { + for (const e of ctx.edgeObjects) e.deselect() + ctx.renderInterLayerEdgesFlag = true + redrawIntraLayerEdges() +} + +// Settings setters (v2 Shiny handlers) ===== + +export function toggleDirection(enabled: boolean): void { + ctx.isDirectionEnabled = enabled + ctx.renderInterLayerEdgesFlag = true + redrawIntraLayerEdges() +} + +export function setIntraDirectionArrowSize(size: number): void { + ctx.intraDirectionArrowSize = size + redrawIntraLayerEdges() +} + +export function setInterDirectionArrowSize(size: number): void { + ctx.interDirectionArrowSize = size + ctx.renderInterLayerEdgesFlag = true +} + +export function setEdgeWidthByWeight(flag: boolean): void { + ctx.edgeWidthByWeight = flag + ctx.renderInterLayerEdgesFlag = true + redrawIntraLayerEdges() +} + +export function setIntraLayerEdgeOpacity(opacity: number): void { + ctx.intraLayerEdgeOpacity = opacity + redrawIntraLayerEdges() +} + +export function setInterLayerEdgeOpacity(opacity: number): void { + ctx.interLayerEdgeOpacity = opacity + ctx.renderInterLayerEdgesFlag = true +} + +export function setEdgeSelectedColorPriority(flag: boolean): void { + ctx.selectedEdgeColorFlag = flag + ctx.renderInterLayerEdgesFlag = true + redrawIntraLayerEdges() +} + +export function setEdgeFileColorPriority(flag: boolean): void { + ctx.edgeFileColorPriority = flag + ctx.renderInterLayerEdgesFlag = true + redrawIntraLayerEdges() + // v2 also showed/hid the channel color-picker div — Phase 13 UI +} + +export function setIntraChannelCurvature(curvature: number): void { + ctx.intraChannelCurvature = curvature + redrawIntraLayerEdges() +} + +export function setInterChannelCurvature(curvature: number): void { + ctx.interChannelCurvature = curvature + ctx.renderInterLayerEdgesFlag = true +} diff --git a/frontend/src/actions/screen.ts b/frontend/src/actions/screen.ts index 41412fc..1e6ec24 100644 --- a/frontend/src/actions/screen.ts +++ b/frontend/src/actions/screen.ts @@ -3,6 +3,7 @@ import * as THREE from 'three' import { ctx } from '../three' +import { renderInterLayerEdges } from './edge' // v2 global raycaster (config/global_variables.js). Reused each frame. const RAYCASTER = new THREE.Raycaster() @@ -74,9 +75,9 @@ export function raycaster(): THREE.Raycaster { export function animate(): void { setTimeout(() => requestAnimationFrame(animate), 1000 / ctx.fps) - // ponytail: inter-layer-edge + CSS2D label rendering hook in here once those - // actions land (v2 called renderInterLayerEdges / renderLayerLabels / - // renderNodeLabels before render). Empty scene renders fine without them. + // ponytail: CSS2D label rendering hooks in here once labels.ts lands (v2 + // called renderLayerLabels / renderNodeLabels before render). + renderInterLayerEdges() if (ctx.scene && ctx.camera) ctx.renderer?.render(ctx.scene.THREE_Object, ctx.camera) } diff --git a/frontend/src/commands/scene.ts b/frontend/src/commands/scene.ts index c3d4e71..fb8fbdf 100644 --- a/frontend/src/commands/scene.ts +++ b/frontend/src/commands/scene.ts @@ -7,6 +7,7 @@ import { bus } from '../bus' import type { Command } from './base' +import { redrawIntraLayerEdges, redrawInterLayerEdges } from '../actions/edge' import { ctx, COLOR_VECTOR_280, @@ -21,14 +22,6 @@ function clusterColor(id: number): string { return COLOR_VECTOR_280[id % COLOR_VECTOR_280.length] } -function redrawIntraLayerEdges(): void { - for (const e of ctx.edgeObjects) if (!e.interLayer) e.redrawEdge() -} - -function redrawInterLayerEdges(): void { - for (const e of ctx.edgeObjects) if (e.interLayer) e.redrawEdge() -} - export class ChangeNodeColorCommand implements Command { description: string private readonly oldColor: string diff --git a/frontend/src/three/runtime.ts b/frontend/src/three/runtime.ts index 3e8e215..3d5646b 100644 --- a/frontend/src/three/runtime.ts +++ b/frontend/src/three/runtime.ts @@ -70,6 +70,12 @@ export interface RuntimeContext { selectedNodeColorFlag: boolean showAllNodeLabelsFlag: boolean showSelectedNodeLabelsFlag: boolean + + // Inter-layer edge render machinery (v2 edge.js globals) + renderInterLayerEdgesFlag: boolean + waitEdgeRenderFlag: boolean + interEdgesRemoved: boolean + interLayerEdgesRenderPauseFlag: boolean } const winW = typeof window !== 'undefined' ? window.innerWidth : 800 @@ -119,6 +125,11 @@ export const ctx: RuntimeContext = { selectedNodeColorFlag: true, showAllNodeLabelsFlag: false, showSelectedNodeLabelsFlag: true, + + renderInterLayerEdgesFlag: false, + waitEdgeRenderFlag: true, + interEdgesRemoved: false, + interLayerEdgesRenderPauseFlag: false, } // Reset registries + tunables to defaults (used by tests and network reload). @@ -151,6 +162,10 @@ export function resetContext(): void { ctx.selectedNodeColorFlag = true ctx.showAllNodeLabelsFlag = false ctx.showSelectedNodeLabelsFlag = true + ctx.renderInterLayerEdgesFlag = false + ctx.waitEdgeRenderFlag = true + ctx.interEdgesRemoved = false + ctx.interLayerEdgesRenderPauseFlag = false } // Registry snapshot — the mutable scene-graph state a network load replaces. From 439c36fd609fc37c132b7e398141261f2060a28f Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 10:09:22 +0100 Subject: [PATCH 023/166] =?UTF-8?q?frontend:=20layer=20action=20=E2=80=94?= =?UTF-8?q?=20spread,=20hover,=20selection,=20visibility=20(Phase=2012=20p?= =?UTF-8?q?artial)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port of www/js/object_actions/layer.js pure parts: initialSpreadLayers moves here from network.ts, raycaster hover with red highlight + repaint, double-click/selectLayer/select-all selection synced to the store, repaintLayers with default/picker color priority, per-layer visibility and node-label toggles that recompute node label flags, plus the coords/opacity/wireframe/color-priority setters. ctx gains lastHoveredLayerIndex, hoveredLayerPaintedFlag, renderLayerLabelsFlag. Checkbox group + color-picker DOM stay with Phase 13; DragControls and the held-key/slider rotate/move/scale intervals with canvas_controls.ts. Co-Authored-By: Claude Fable 5 --- PLAN.md | 4 +- frontend/src/actions/layer.test.ts | 91 +++++++++++++++++++ frontend/src/actions/layer.ts | 137 +++++++++++++++++++++++++++++ frontend/src/actions/network.ts | 14 +-- frontend/src/three/runtime.ts | 12 +++ 5 files changed, 243 insertions(+), 15 deletions(-) create mode 100644 frontend/src/actions/layer.test.ts create mode 100644 frontend/src/actions/layer.ts diff --git a/PLAN.md b/PLAN.md index 7e3a89b..c3d1393 100644 --- a/PLAN.md +++ b/PLAN.md @@ -147,10 +147,10 @@ Done now (portable core): Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2D) and Phase 13 (UI/DOM), then delete `www/js/object_actions/`: - [ ] `screen.js` → `src/actions/screen.ts` (renderer, camera, window bounds, raycaster, `animate()`) -- [x] `network.js` → `src/actions/network.ts` — `buildNetwork` consumes backend `NetworkData` (layer/edge limits enforced server-side; channel limit checked client-side); `loadNetwork` wraps it in `LoadNetworkCommand`. Builds a fresh `Scene` + registries (never mutates in place) so undo snapshots stay valid. Includes interim `initialSpreadLayers` — moves to `layer.ts` when it ports. 4 Vitest tests. *(DragControls/labels/channel-UI/importNetwork stay with their own actions.)* +- [x] `network.js` → `src/actions/network.ts` — `buildNetwork` consumes backend `NetworkData` (layer/edge limits enforced server-side; channel limit checked client-side); `loadNetwork` wraps it in `LoadNetworkCommand`. Builds a fresh `Scene` + registries (never mutates in place) so undo snapshots stay valid. Spread/scramble now imported from `layer.ts`/`node.ts`. 4 Vitest tests. *(DragControls/labels/channel-UI/importNetwork stay with their own actions.)* - [x] `node.js` → `src/actions/node.ts` — hover (`checkHoverOverNode`), double-click + search + select-all selection (store-synced), `repaintNode` (cluster branch folded into `Node.getColor`), label-flag priorities (DOM checkboxes → `Layer.isVisible`), shape/color-priority setters; `scrambleNodes` moved here from `network.ts`. New ctx state: `lastHoveredNodeIndex`, `selectedNodeColorFlag`, `showAll/showSelectedNodeLabelsFlag`. 7 Vitest tests. *(Search bar/sliders/descr div → Phase 13; lasso + held-key/spread/move/scale → canvas_controls.ts; attribute upload → POST /api/attributes.)* - [x] `edge.js` → `src/actions/edge.ts` — inter-layer render machinery (`renderInterLayerEdges` wired into `animate()`, remove-on-drag/pause/zero-opacity, v2 locked-flag redraw pair), `redrawIntra/InterLayerEdges` (moved out of `commands/scene.ts` privates), channel color/visibility, `unselectAllEdges`, all edge-setting setters. New ctx state: `renderInterLayerEdgesFlag`, `waitEdgeRenderFlag`, `interEdgesRemoved`, `interLayerEdgesRenderPauseFlag`. 5 Vitest tests. *(Edge build lives in network.ts; channel UI lists/picker → Phase 13; attributes upload → POST /api/attributes.)* -- [ ] `layer.js` → `src/actions/layer.ts` (spread/move + DragControls + layer checkboxes) +- [x] `layer.js` → `src/actions/layer.ts` — `initialSpreadLayers` (moved from network.ts), hover (`checkHoverOverLayer` red-highlight + repaint), double-click/`selectLayer`/select-all selection (store-synced), `repaintLayers` (selected → default/picker priority), per-layer visibility + node-label toggles (recompute node label flags), coords/opacity/wireframe/color-priority setters. New ctx state: `lastHoveredLayerIndex`, `hoveredLayerPaintedFlag`, `renderLayerLabelsFlag`. 6 Vitest tests. *(Checkbox group + picker DOM → Phase 13; DragControls + held-key/slider rotate/move/scale intervals → canvas_controls.ts.)* - [ ] `themes.js` → `src/actions/themes.ts` (needs renderer `setClearColor`; listens to `theme:changed`) - [ ] `labels.js` → `src/actions/labels.ts` (CSS2D label rendering in the animate loop) - [ ] `canvas_controls.js` → `src/actions/canvas_controls.ts` (mouse/keyboard canvas controls, 510 lines, heavy DOM) diff --git a/frontend/src/actions/layer.test.ts b/frontend/src/actions/layer.test.ts new file mode 100644 index 0000000..6c5305f --- /dev/null +++ b/frontend/src/actions/layer.test.ts @@ -0,0 +1,91 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import { store } from '../store' +import { + ctx, + Layer, + resetContext, + SELECTED_LAYER_DEFAULT_COLOR, +} from '../three' +import { + getSelectedLayers, + initialSpreadLayers, + repaintLayers, + selectAllLayers, + selectLayer, + setLayerVisibility, +} from './layer' + +function seed(n: number): void { + ctx.layers = Array.from( + { length: n }, + (_, i) => new Layer({ id: i, name: `L${i + 1}` }) + ) + ctx.layerGroups = Object.fromEntries(ctx.layers.map((l, i) => [l.name, i])) +} + +beforeEach(() => { + resetContext() + store.update({ selectedLayers: [] }) +}) + +describe('initialSpreadLayers', () => { + it('spreads an odd count symmetrically around 0', () => { + seed(3) + initialSpreadLayers() + const xs = ctx.layers.map((l) => l.getPosition('x')) + expect(xs[1]).toBe(0) + expect(xs[0]).toBe(-xs[2]) + expect(xs[0]).toBeLessThan(0) + }) + + it('direction -1 stacks an even count back to 0', () => { + seed(2) + initialSpreadLayers(1) + expect(ctx.layers[0].getPosition('x')).not.toBe(0) + initialSpreadLayers(-1) + expect(ctx.layers[0].getPosition('x')).toBeCloseTo(0) + }) +}) + +describe('selection', () => { + it('selectLayer repaints selected color and syncs the store', () => { + seed(2) + selectLayer(0, true) + expect(getSelectedLayers()).toEqual([0]) + expect(ctx.layers[0].color).toBe(SELECTED_LAYER_DEFAULT_COLOR) + expect(ctx.layers[1].color).not.toBe(SELECTED_LAYER_DEFAULT_COLOR) + expect(store.get().selectedLayers).toEqual(['L1']) + expect(ctx.renderLayerLabelsFlag).toBe(true) + }) + + it('selectAllLayers(false) restores imported colors', () => { + seed(2) + selectAllLayers(true) + expect(getSelectedLayers()).toEqual([0, 1]) + selectAllLayers(false) + expect(getSelectedLayers()).toEqual([]) + expect(ctx.layers[0].color).toBe(ctx.layers[0].importedColor) + expect(store.get().selectedLayers).toEqual([]) + }) +}) + +describe('repaintLayers', () => { + it('picker priority falls back to current color without the DOM input', () => { + seed(1) + ctx.layers[0].setColor('#ababab') + ctx.layerColorPrioritySource = 'picker' + repaintLayers() + expect(ctx.layers[0].color).toBe('#ababab') + }) +}) + +describe('setLayerVisibility', () => { + it('hides the plane, raises render flags and recomputes node label flags', () => { + seed(1) + setLayerVisibility(0, false) + expect(ctx.layers[0].isVisible).toBe(false) + expect(ctx.layers[0].plane.visible).toBe(false) + expect(ctx.renderInterLayerEdgesFlag).toBe(true) + expect(ctx.renderLayerLabelsFlag).toBe(true) + }) +}) diff --git a/frontend/src/actions/layer.ts b/frontend/src/actions/layer.ts new file mode 100644 index 0000000..d7f90a4 --- /dev/null +++ b/frontend/src/actions/layer.ts @@ -0,0 +1,137 @@ +// Port of v2 www/js/object_actions/layer.js — spread, hover, selection, +// repaint, visibility and layer-wide setters. Selection syncs to the store +// instead of Shiny inputs. Deferred with their phases: checkbox group + +// color-picker DOM (Phase 13 UI), DragControls + held-key/slider +// rotate/move/scale intervals (canvas_controls.ts). + +import type { MeshBasicMaterial } from 'three' +import { store } from '../store' +import { ctx, SELECTED_LAYER_DEFAULT_COLOR } from '../three' +import type { ColorPrioritySource } from '../three' +import { findIndexByUuid } from '../utils' +import { decideNodeLabelFlags } from './node' +import { raycaster, setRaycaster } from './screen' + +// Spread layers evenly along x (v2: predefined layouts, upload/import, +// canvas controls; direction -1 stacks them back). +export function initialSpreadLayers(direction = 1): void { + const n = ctx.layers.length + const spacing = (ctx.xBoundMax * 2) / n + ctx.layers.forEach((layer, i) => { + const offset = + n % 2 + ? (i - Math.floor(n / 2)) * spacing + : (i - n / 2) * spacing + spacing / 2 + layer.translateX(direction * offset) + }) +} + +function updateSelectedLayersStore(): void { + store.update({ + selectedLayers: ctx.layers.filter((l) => l.isSelected).map((l) => l.name), + }) +} + +// @return selected layer indexes +export function getSelectedLayers(): number[] { + return ctx.layers.filter((l) => l.isSelected).map((l) => l.id) +} + +export function repaintLayers(): void { + for (const layer of ctx.layers) { + if (layer.isSelected) layer.setColor(SELECTED_LAYER_DEFAULT_COLOR) + else if (ctx.layerColorPrioritySource === 'default') + layer.setColor(layer.importedColor) + else if (ctx.layerColorPrioritySource === 'picker') + // ponytail: DOM picker read, as in Layer.getColor; the input only + // exists in the browser UI (Phase 13). Falls back to the stored color. + layer.setColor( + (typeof document !== 'undefined' && + (document.getElementById('floor_color') as HTMLInputElement | null) + ?.value) || + layer.color + ) + } +} + +export function checkHoverOverLayer(event: { + clientX: number + clientY: number +}): void { + setRaycaster(event) + const planes = ctx.layers.map(({ plane }) => plane) + const intersects = raycaster().intersectObjects(planes) + if (intersects.length > 0) { + if (ctx.lastHoveredLayerIndex !== null) { + repaintLayers() + ctx.hoveredLayerPaintedFlag = true + } + const i = findIndexByUuid(planes, intersects[0].object.uuid) + ;(ctx.layers[i].plane.material as MeshBasicMaterial).color.set(0xff0000) + ctx.lastHoveredLayerIndex = i + } else { + if (ctx.hoveredLayerPaintedFlag) { + repaintLayers() // remove red color from last hovered + ctx.hoveredLayerPaintedFlag = false + } + ctx.lastHoveredLayerIndex = null + } +} + +// v2 toggled the matching DOM checkbox too — Phase 13 wires that back up. +export function performDoubleClickLayerSelection(): void { + if (ctx.lastHoveredLayerIndex === null) return + ctx.layers[ctx.lastHoveredLayerIndex].toggleSelection() + ctx.lastHoveredLayerIndex = null + ctx.renderLayerLabelsFlag = true + repaintLayers() + updateSelectedLayersStore() +} + +// v2 selectCheckedLayer minus the DOM checkbox element. +export function selectLayer(index: number, selected: boolean): void { + ctx.layers[index].isSelected = selected + ctx.renderLayerLabelsFlag = true + repaintLayers() + updateSelectedLayersStore() +} + +export function selectAllLayers(selected: boolean): void { + for (const layer of ctx.layers) layer.isSelected = selected + ctx.renderLayerLabelsFlag = true + repaintLayers() + updateSelectedLayersStore() +} + +// v2 hideLayers core, per layer (the checkbox loop is Phase 13's). +export function setLayerVisibility(index: number, visible: boolean): void { + ctx.layers[index].toggleVisibility(visible) + ctx.renderInterLayerEdgesFlag = true + ctx.renderLayerLabelsFlag = true + decideNodeLabelFlags() +} + +// v2 showLayerNodeLabels core, per layer. +export function setLayerNodeLabels(index: number, show: boolean): void { + ctx.layers[index].showNodeLabels = show + decideNodeLabelFlags() +} + +// Handlers (v2 Shiny handlers) ===== + +export function showLayerCoords(show: boolean): void { + for (const layer of ctx.layers) layer.toggleCoords(show) +} + +export function setFloorOpacity(opacity: number): void { + for (const layer of ctx.layers) layer.setOpacity(opacity) +} + +export function showWireFrames(flag: boolean): void { + for (const layer of ctx.layers) layer.toggleWireframe(flag) +} + +export function setLayerColorPriority(source: ColorPrioritySource): void { + ctx.layerColorPrioritySource = source + repaintLayers() +} diff --git a/frontend/src/actions/network.ts b/frontend/src/actions/network.ts index 25923a8..428712a 100644 --- a/frontend/src/actions/network.ts +++ b/frontend/src/actions/network.ts @@ -17,6 +17,7 @@ import { resetContext, Scene, } from '../three' +import { initialSpreadLayers } from './layer' import { scrambleNodes } from './node' // v2 uploadNetwork(): one undoable step wrapping the full build. @@ -59,19 +60,6 @@ function initializeLayers(names: string[]): void { initialSpreadLayers() } -// v2 layer.js initialSpreadLayers — lives here until actions/layer.ts is ported. -export function initialSpreadLayers(direction = 1): void { - const n = ctx.layers.length - const spacing = (ctx.xBoundMax * 2) / n - ctx.layers.forEach((layer, i) => { - const offset = - n % 2 - ? (i - Math.floor(n / 2)) * spacing - : (i - n / 2) * spacing + spacing / 2 - layer.translateX(direction * offset) - }) -} - function initializeChannels(channels: string[]): void { const palette = store.get().config?.channel_colors_light ?? [] channels.forEach((channel, i) => { diff --git a/frontend/src/three/runtime.ts b/frontend/src/three/runtime.ts index 3d5646b..f7f52ba 100644 --- a/frontend/src/three/runtime.ts +++ b/frontend/src/three/runtime.ts @@ -76,6 +76,11 @@ export interface RuntimeContext { waitEdgeRenderFlag: boolean interEdgesRemoved: boolean interLayerEdgesRenderPauseFlag: boolean + + // Layer hover/label state (v2 layer.js globals) + lastHoveredLayerIndex: number | null + hoveredLayerPaintedFlag: boolean + renderLayerLabelsFlag: boolean } const winW = typeof window !== 'undefined' ? window.innerWidth : 800 @@ -130,6 +135,10 @@ export const ctx: RuntimeContext = { waitEdgeRenderFlag: true, interEdgesRemoved: false, interLayerEdgesRenderPauseFlag: false, + + lastHoveredLayerIndex: null, + hoveredLayerPaintedFlag: false, + renderLayerLabelsFlag: false, } // Reset registries + tunables to defaults (used by tests and network reload). @@ -166,6 +175,9 @@ export function resetContext(): void { ctx.waitEdgeRenderFlag = true ctx.interEdgesRemoved = false ctx.interLayerEdgesRenderPauseFlag = false + ctx.lastHoveredLayerIndex = null + ctx.hoveredLayerPaintedFlag = false + ctx.renderLayerLabelsFlag = false } // Registry snapshot — the mutable scene-graph state a network load replaces. From a1ae9b5419c4c93de07154c31f4b3f4ec84179e0 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 10:13:54 +0100 Subject: [PATCH 024/166] frontend: themes action listening to theme:changed (Phase 12 partial) Port of www/js/object_actions/themes.js: THEMES presets (light/dark/gray from the v2 buttons), applyTheme sets renderer clear color, ctx.edgeDefaultColor/labelColor, reassigns channel colors from the config palette and repaints layers picker-priority with the theme floor color. registerThemeListener() subscribes to theme:changed in main.ts, so ChangeThemeCommand execute/undo now performs the concrete recolour. Supporting: ctx.labelColor (v2 globalLabelColor), repaintLayers gains an optional pickerColor override, assignChannelColorsFromPalette lands in edge.ts. Theme buttons DOM + channel edit list stay with Phase 13; label recolour with labels.ts. Co-Authored-By: Claude Fable 5 --- PLAN.md | 4 +- frontend/src/actions/edge.ts | 8 +++ frontend/src/actions/layer.ts | 13 ++--- frontend/src/actions/themes.test.ts | 65 ++++++++++++++++++++++++ frontend/src/actions/themes.ts | 76 +++++++++++++++++++++++++++++ frontend/src/main.ts | 2 + frontend/src/three/runtime.ts | 6 +++ 7 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 frontend/src/actions/themes.test.ts create mode 100644 frontend/src/actions/themes.ts diff --git a/PLAN.md b/PLAN.md index c3d1393..a033036 100644 --- a/PLAN.md +++ b/PLAN.md @@ -131,7 +131,7 @@ All 8 in `src/commands/scene.ts`. Commands are **thin**: they apply/reverse alre - [x] Implement `ChangeNodeColorCommand` - [x] Implement `ChangeNodeSizeCommand` - [x] Implement `ChangeEdgeColorCommand` — captures colors/importedColors + `edgeFileColorPriority`, redraws -- [x] Implement `ChangeThemeCommand` — store-only (getter/setter injected); concrete recolour is the Phase 11 themes action listening to `theme:changed` +- [x] Implement `ChangeThemeCommand` — store-only (getter/setter injected); concrete recolor is the Phase 11 themes action listening to `theme:changed` - [x] Write Vitest tests for execute/undo/redo on each command (9 tests in `src/commands/scene.test.ts`) --- @@ -151,7 +151,7 @@ Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2 - [x] `node.js` → `src/actions/node.ts` — hover (`checkHoverOverNode`), double-click + search + select-all selection (store-synced), `repaintNode` (cluster branch folded into `Node.getColor`), label-flag priorities (DOM checkboxes → `Layer.isVisible`), shape/color-priority setters; `scrambleNodes` moved here from `network.ts`. New ctx state: `lastHoveredNodeIndex`, `selectedNodeColorFlag`, `showAll/showSelectedNodeLabelsFlag`. 7 Vitest tests. *(Search bar/sliders/descr div → Phase 13; lasso + held-key/spread/move/scale → canvas_controls.ts; attribute upload → POST /api/attributes.)* - [x] `edge.js` → `src/actions/edge.ts` — inter-layer render machinery (`renderInterLayerEdges` wired into `animate()`, remove-on-drag/pause/zero-opacity, v2 locked-flag redraw pair), `redrawIntra/InterLayerEdges` (moved out of `commands/scene.ts` privates), channel color/visibility, `unselectAllEdges`, all edge-setting setters. New ctx state: `renderInterLayerEdgesFlag`, `waitEdgeRenderFlag`, `interEdgesRemoved`, `interLayerEdgesRenderPauseFlag`. 5 Vitest tests. *(Edge build lives in network.ts; channel UI lists/picker → Phase 13; attributes upload → POST /api/attributes.)* - [x] `layer.js` → `src/actions/layer.ts` — `initialSpreadLayers` (moved from network.ts), hover (`checkHoverOverLayer` red-highlight + repaint), double-click/`selectLayer`/select-all selection (store-synced), `repaintLayers` (selected → default/picker priority), per-layer visibility + node-label toggles (recompute node label flags), coords/opacity/wireframe/color-priority setters. New ctx state: `lastHoveredLayerIndex`, `hoveredLayerPaintedFlag`, `renderLayerLabelsFlag`. 6 Vitest tests. *(Checkbox group + picker DOM → Phase 13; DragControls + held-key/slider rotate/move/scale intervals → canvas_controls.ts.)* -- [ ] `themes.js` → `src/actions/themes.ts` (needs renderer `setClearColor`; listens to `theme:changed`) +- [x] `themes.js` → `src/actions/themes.ts` — `THEMES` presets (light/dark/gray from v2 buttons), `applyTheme(name, fromInit)` (renderer clear color, `ctx.edgeDefaultColor`/`labelColor`, channel palette from config, picker-priority layer repaint with theme floor color), `registerThemeListener()` wired to `theme:changed` in main.ts so `ChangeThemeCommand` execute/undo recolors. Added `ctx.labelColor` (v2 `globalLabelColor`), `repaintLayers(pickerColor?)`, `assignChannelColorsFromPalette` in edge.ts. 4 Vitest tests. *(Theme buttons DOM + channel edit list rebuild → Phase 13; label recolor → labels.ts.)* - [ ] `labels.js` → `src/actions/labels.ts` (CSS2D label rendering in the animate loop) - [ ] `canvas_controls.js` → `src/actions/canvas_controls.ts` (mouse/keyboard canvas controls, 510 lines, heavy DOM) - [ ] `right_click_menu.js` → `src/actions/right_click_menu.ts` (DOM context menu) diff --git a/frontend/src/actions/edge.ts b/frontend/src/actions/edge.ts index c4856b2..4f32d79 100644 --- a/frontend/src/actions/edge.ts +++ b/frontend/src/actions/edge.ts @@ -7,6 +7,14 @@ import { ctx } from '../three' +// v2 assignChannelColorsFromPalette — reassigns every known channel's color +// in insertion order (channel order from the network build). +export function assignChannelColorsFromPalette(palette: string[]): void { + Object.keys(ctx.channelColors).forEach((channel, i) => { + ctx.channelColors[channel] = palette[i % palette.length] + }) +} + export function redrawIntraLayerEdges(): void { for (const e of ctx.edgeObjects) if (!e.interLayer) e.redrawEdge() } diff --git a/frontend/src/actions/layer.ts b/frontend/src/actions/layer.ts index d7f90a4..50095e7 100644 --- a/frontend/src/actions/layer.ts +++ b/frontend/src/actions/layer.ts @@ -37,18 +37,19 @@ export function getSelectedLayers(): number[] { return ctx.layers.filter((l) => l.isSelected).map((l) => l.id) } -export function repaintLayers(): void { +// `pickerColor` overrides the DOM color input (themes.ts passes the theme's +// floor color; the input itself only exists once Phase 13 builds the UI). +export function repaintLayers(pickerColor?: string): void { for (const layer of ctx.layers) { if (layer.isSelected) layer.setColor(SELECTED_LAYER_DEFAULT_COLOR) else if (ctx.layerColorPrioritySource === 'default') layer.setColor(layer.importedColor) else if (ctx.layerColorPrioritySource === 'picker') - // ponytail: DOM picker read, as in Layer.getColor; the input only - // exists in the browser UI (Phase 13). Falls back to the stored color. layer.setColor( - (typeof document !== 'undefined' && - (document.getElementById('floor_color') as HTMLInputElement | null) - ?.value) || + pickerColor || + (typeof document !== 'undefined' && + (document.getElementById('floor_color') as HTMLInputElement | null) + ?.value) || layer.color ) } diff --git a/frontend/src/actions/themes.test.ts b/frontend/src/actions/themes.test.ts new file mode 100644 index 0000000..99e86c7 --- /dev/null +++ b/frontend/src/actions/themes.test.ts @@ -0,0 +1,65 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import type { AppConfig } from '../api/config' +import { history } from '../commands/base' +import { ChangeThemeCommand } from '../commands/scene' +import { store } from '../store' +import { ctx, Layer, resetContext, Scene } from '../three' +import { applyTheme, registerThemeListener, THEMES } from './themes' + +beforeEach(() => { + resetContext() + ctx.scene = new Scene() + ctx.layers = [new Layer({ id: 0, name: 'L1' })] + ctx.channelColors = { ch1: '#000001', ch2: '#000002' } + store.update({ + currentTheme: 'dark', + config: { + channel_colors_light: ['#l1', '#l2'], + channel_colors_dark: ['#d1', '#d2'], + } as unknown as AppConfig, + }) +}) + +describe('applyTheme', () => { + it('recolours edges/labels/channels and repaints layers with the floor color', () => { + applyTheme('light') + expect(ctx.edgeDefaultColor).toBe(THEMES.light.edge) + expect(ctx.labelColor).toBe('#000000') + expect(ctx.channelColors).toEqual({ ch1: '#d1', ch2: '#d2' }) + expect(ctx.layerColorPrioritySource).toBe('picker') + expect(ctx.layers[0].color).toBe(THEMES.light.floor) + expect(ctx.renderInterLayerEdgesFlag).toBe(true) + }) + + it('fromInit applies colors but skips the repaint pass', () => { + applyTheme('dark', true) + expect(ctx.edgeDefaultColor).toBe('#ffffff') + expect(ctx.channelColors).toEqual({ ch1: '#l1', ch2: '#l2' }) + expect(ctx.layerColorPrioritySource).toBe('default') + expect(ctx.renderInterLayerEdgesFlag).toBe(false) + }) + + it('unknown theme is a no-op', () => { + applyTheme('neon') + expect(ctx.channelColors).toEqual({ ch1: '#000001', ch2: '#000002' }) + }) +}) + +describe('theme:changed wiring', () => { + it('ChangeThemeCommand execute/undo re-applies themes through the bus', () => { + registerThemeListener() + history.execute( + new ChangeThemeCommand( + 'light', + () => store.get().currentTheme, + (t) => store.update({ currentTheme: t }) + ) + ) + expect(store.get().currentTheme).toBe('light') + expect(ctx.labelColor).toBe('#000000') + + history.undo() + expect(store.get().currentTheme).toBe('dark') + expect(ctx.labelColor).toBe('#ffffff') + }) +}) diff --git a/frontend/src/actions/themes.ts b/frontend/src/actions/themes.ts new file mode 100644 index 0000000..0ea8984 --- /dev/null +++ b/frontend/src/actions/themes.ts @@ -0,0 +1,76 @@ +// Port of v2 www/js/object_actions/themes.js. The theme buttons (Phase 13 +// UI) dispatch ChangeThemeCommand; its theme:changed event lands here and +// applyTheme does the concrete recolour. Channel palettes come from +// /api/config (v2 CHANNEL_COLORS_LIGHT/DARK). + +import { bus } from '../bus' +import { store } from '../store' +import { ctx } from '../three' +import { assignChannelColorsFromPalette, redrawIntraLayerEdges } from './edge' +import { repaintLayers } from './layer' +import { setRendererColor } from './screen' + +export interface Theme { + background: string + floor: string + edge: string + channelPalette: 'light' | 'dark' + label: string +} + +// v2 attachThemeButtons' three presets. +export const THEMES: Record = { + light: { + background: '#ffffff', + floor: '#8aa185', + edge: '#5c5c5c', + channelPalette: 'dark', + label: '#000000', + }, + dark: { + background: '#000000', + floor: '#777777', + edge: '#ffffff', + channelPalette: 'light', + label: '#ffffff', + }, + gray: { + background: '#999999', + floor: '#1d4991', + edge: '#6e2a5a', + channelPalette: 'light', + label: '#ffffff', + }, +} + +export function applyTheme(name: string, fromInit = false): void { + const theme = THEMES[name] + if (!theme || !ctx.scene?.exists()) return + + setRendererColor(theme.background) + ctx.edgeDefaultColor = theme.edge + ctx.labelColor = theme.label + + const config = store.get().config + const palette = + theme.channelPalette === 'dark' + ? config?.channel_colors_dark + : config?.channel_colors_light + if (palette) assignChannelColorsFromPalette(palette) + + if (!fromInit) { + // v2 repaintLayersFromPicker: switch layers to picker priority with the + // theme's floor color + ctx.layerColorPrioritySource = 'picker' + repaintLayers(theme.floor) + redrawIntraLayerEdges() + ctx.renderInterLayerEdgesFlag = true + // v2 also rebuilt the channel edit list + label colors — Phase 13 UI / + // labels.ts react to the same theme:changed event + } +} + +// Called once from main.ts. +export function registerThemeListener(): void { + bus.on('theme:changed', ({ theme }) => applyTheme(theme)) +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 0c6886d..be709f1 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -7,6 +7,7 @@ import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' import { setRenderer, resetScreen, animate } from './actions/screen' +import { registerThemeListener } from './actions/themes' import { registerGlobalListeners } from './event_listeners' async function main(): Promise { @@ -34,6 +35,7 @@ async function main(): Promise { } registerGlobalListeners() + registerThemeListener() animate() // Test hook: lets Playwright read scene state via page.evaluate (WebGL is diff --git a/frontend/src/three/runtime.ts b/frontend/src/three/runtime.ts index f7f52ba..283b499 100644 --- a/frontend/src/three/runtime.ts +++ b/frontend/src/three/runtime.ts @@ -81,6 +81,9 @@ export interface RuntimeContext { lastHoveredLayerIndex: number | null hoveredLayerPaintedFlag: boolean renderLayerLabelsFlag: boolean + + // Universal label color (v2 globalLabelColor; read by labels.ts) + labelColor: string } const winW = typeof window !== 'undefined' ? window.innerWidth : 800 @@ -139,6 +142,8 @@ export const ctx: RuntimeContext = { lastHoveredLayerIndex: null, hoveredLayerPaintedFlag: false, renderLayerLabelsFlag: false, + + labelColor: '#ffffff', } // Reset registries + tunables to defaults (used by tests and network reload). @@ -178,6 +183,7 @@ export function resetContext(): void { ctx.lastHoveredLayerIndex = null ctx.hoveredLayerPaintedFlag = false ctx.renderLayerLabelsFlag = false + ctx.labelColor = '#ffffff' } // Registry snapshot — the mutable scene-graph state a network load replaces. From 78f120083e59a8ab709a8c0d203d9153fa78af11 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 10:19:16 +0100 Subject: [PATCH 025/166] frontend: labels action rendering in the animate loop (Phase 12 partial) Port of www/js/object_actions/labels.js keeping v2's plain absolutely-positioned divs in #labelDiv (no CSS2DRenderer dep; inline position styling until the Phase 13 stylesheet). createLabels runs on network build; flag-gated renderLayerLabels/renderNodeLabels register as animate hooks via a new registerAnimateHook in screen.ts, which avoids a screen->labels->node->screen import cycle. showLayer/NodeLabels modes, label resize and setLabelColor ported; themes.ts now recolors labels on theme change and decideNodeLabelFlags raises the node render flag as v2 did. ctx gains renderNodeLabelsFlag + the two layer-label flags. Co-Authored-By: Claude Fable 5 --- PLAN.md | 2 +- frontend/index.html | 3 +- frontend/src/actions/labels.test.ts | 70 +++++++++++++ frontend/src/actions/labels.ts | 154 ++++++++++++++++++++++++++++ frontend/src/actions/network.ts | 2 + frontend/src/actions/node.ts | 1 + frontend/src/actions/screen.ts | 10 +- frontend/src/actions/themes.ts | 5 +- frontend/src/main.ts | 2 + frontend/src/three/runtime.ts | 9 ++ 10 files changed, 252 insertions(+), 6 deletions(-) create mode 100644 frontend/src/actions/labels.test.ts create mode 100644 frontend/src/actions/labels.ts diff --git a/PLAN.md b/PLAN.md index a033036..185dc7f 100644 --- a/PLAN.md +++ b/PLAN.md @@ -152,7 +152,7 @@ Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2 - [x] `edge.js` → `src/actions/edge.ts` — inter-layer render machinery (`renderInterLayerEdges` wired into `animate()`, remove-on-drag/pause/zero-opacity, v2 locked-flag redraw pair), `redrawIntra/InterLayerEdges` (moved out of `commands/scene.ts` privates), channel color/visibility, `unselectAllEdges`, all edge-setting setters. New ctx state: `renderInterLayerEdgesFlag`, `waitEdgeRenderFlag`, `interEdgesRemoved`, `interLayerEdgesRenderPauseFlag`. 5 Vitest tests. *(Edge build lives in network.ts; channel UI lists/picker → Phase 13; attributes upload → POST /api/attributes.)* - [x] `layer.js` → `src/actions/layer.ts` — `initialSpreadLayers` (moved from network.ts), hover (`checkHoverOverLayer` red-highlight + repaint), double-click/`selectLayer`/select-all selection (store-synced), `repaintLayers` (selected → default/picker priority), per-layer visibility + node-label toggles (recompute node label flags), coords/opacity/wireframe/color-priority setters. New ctx state: `lastHoveredLayerIndex`, `hoveredLayerPaintedFlag`, `renderLayerLabelsFlag`. 6 Vitest tests. *(Checkbox group + picker DOM → Phase 13; DragControls + held-key/slider rotate/move/scale intervals → canvas_controls.ts.)* - [x] `themes.js` → `src/actions/themes.ts` — `THEMES` presets (light/dark/gray from v2 buttons), `applyTheme(name, fromInit)` (renderer clear color, `ctx.edgeDefaultColor`/`labelColor`, channel palette from config, picker-priority layer repaint with theme floor color), `registerThemeListener()` wired to `theme:changed` in main.ts so `ChangeThemeCommand` execute/undo recolors. Added `ctx.labelColor` (v2 `globalLabelColor`), `repaintLayers(pickerColor?)`, `assignChannelColorsFromPalette` in edge.ts. 4 Vitest tests. *(Theme buttons DOM + channel edit list rebuild → Phase 13; label recolor → labels.ts.)* -- [ ] `labels.js` → `src/actions/labels.ts` (CSS2D label rendering in the animate loop) +- [x] `labels.js` → `src/actions/labels.ts` — v2's plain absolutely-positioned divs in `#labelDiv` (kept over CSS2DRenderer: no new dep, inline `position:absolute` until the Phase 13 stylesheet), `createLabels` on network build, flag-gated `renderLayer/NodeLabels` registered as animate hooks (`registerAnimateHook` added to screen.ts — avoids screen→labels→node→screen cycle), `showLayer/NodeLabels` modes, resize + `setLabelColor` (themes now recolors labels). Added `ctx.renderNodeLabelsFlag` + layer-label flags; `decideNodeLabelFlags` raises the render flag as v2 did. 4 Vitest tests (flag machinery; div positioning → Phase 13 Playwright). `index.html` gains `#labelDiv`. - [ ] `canvas_controls.js` → `src/actions/canvas_controls.ts` (mouse/keyboard canvas controls, 510 lines, heavy DOM) - [ ] `right_click_menu.js` → `src/actions/right_click_menu.ts` (DOM context menu) - [ ] Delete `www/js/object_actions/` diff --git a/frontend/index.html b/frontend/index.html index 0a91271..7bf32dd 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,4 +1,4 @@ - + @@ -7,6 +7,7 @@
+
diff --git a/frontend/src/actions/labels.test.ts b/frontend/src/actions/labels.test.ts new file mode 100644 index 0000000..030d5d3 --- /dev/null +++ b/frontend/src/actions/labels.test.ts @@ -0,0 +1,70 @@ +// Headless (node-env) tests: flag machinery only. Div creation/positioning +// is exercised by the Phase 13 Playwright E2E against the real DOM. +import { beforeEach, describe, expect, it } from 'vitest' +import { ctx, Layer, Node, resetContext } from '../three' +import { + createLabels, + renderLayerLabels, + renderNodeLabels, + setLabelColor, + showLayerLabels, + showNodeLabels, +} from './labels' + +beforeEach(() => { + resetContext() + ctx.layers = [new Layer({ id: 0, name: 'L1' })] + ctx.layerGroups = { L1: 0 } + ctx.nodeGroups = { A_L1: 'L1' } + ctx.nodeLayerNames = ['A_L1'] + ctx.nodeObjects = [ + new Node({ id: 0, name: 'A', nodeLayerName: 'A_L1', layer: 'L1' }), + ] +}) + +describe('showNodeLabels', () => { + it('maps modes onto the node label flags and recomputes them', () => { + showNodeLabels('all') + expect(ctx.showAllNodeLabelsFlag).toBe(true) + expect(ctx.showSelectedNodeLabelsFlag).toBe(false) + expect(ctx.renderNodeLabelsFlag).toBe(true) + expect(ctx.nodeObjects[0].showLabel).toBe(true) + + showNodeLabels('none') + expect(ctx.showAllNodeLabelsFlag).toBe(false) + expect(ctx.showSelectedNodeLabelsFlag).toBe(false) + expect(ctx.nodeObjects[0].showLabel).toBe(false) + }) +}) + +describe('showLayerLabels', () => { + it('maps modes onto the layer label flags', () => { + showLayerLabels('selected') + expect(ctx.showAllLayerLabelsFlag).toBe(false) + expect(ctx.showSelectedLayerLabelsFlag).toBe(true) + expect(ctx.renderLayerLabelsFlag).toBe(true) + + showLayerLabels('none') + expect(ctx.showAllLayerLabelsFlag).toBe(false) + expect(ctx.showSelectedLayerLabelsFlag).toBe(false) + }) +}) + +describe('animate-loop renderers', () => { + it('run flag-gated and clear their flags (headless no-op on divs)', () => { + createLabels() // no document -> no divs, must not throw + ctx.renderLayerLabelsFlag = true + ctx.renderNodeLabelsFlag = true + renderLayerLabels() + renderNodeLabels() + expect(ctx.renderLayerLabelsFlag).toBe(false) + expect(ctx.renderNodeLabelsFlag).toBe(false) + }) +}) + +describe('setLabelColor', () => { + it('updates the universal label color', () => { + setLabelColor('#ff00ff') + expect(ctx.labelColor).toBe('#ff00ff') + }) +}) diff --git a/frontend/src/actions/labels.ts b/frontend/src/actions/labels.ts new file mode 100644 index 0000000..5318600 --- /dev/null +++ b/frontend/src/actions/labels.ts @@ -0,0 +1,154 @@ +// Port of v2 www/js/object_actions/labels.js — plain absolutely-positioned +// divs in #labelDiv, repositioned in the animate loop (flag-gated, as v2). +// Positioning is set inline so it works before the Phase 13 stylesheet lands. + +import { Vector3 } from 'three' +import { ctx, NODE_LABEL_DEFAULT_SIZE } from '../three' +import { decideNodeLabelFlags } from './node' +import { registerAnimateHook } from './screen' + +const VEC3 = new Vector3() // scratch, reused per frame + +// Rebuilt on every network build (v2 resetValues cleared #labelDiv). +let nodeLabelsDivs: HTMLDivElement[] = [] +let layerLabelsDivs: HTMLDivElement[] = [] + +function labelContainer(): HTMLElement | null { + return typeof document === 'undefined' + ? null + : document.getElementById('labelDiv') +} + +function createLabel( + container: HTMLElement, + text: string, + id: string, + className: string +): HTMLDivElement { + const div = document.createElement('div') + div.textContent = text + div.className = className + div.id = id + div.style.position = 'absolute' + div.style.color = ctx.labelColor + container.appendChild(div) + return div +} + +// Called from the network build (v2 executePostNetworkSetup). No-op headless. +export function createLabels(): void { + const container = labelContainer() + nodeLabelsDivs = [] + layerLabelsDivs = [] + if (!container) return + container.innerHTML = '' + + ctx.nodeObjects.forEach((node, i) => { + const div = createLabel( + container, + node.name, + `${ctx.nodeLayerNames[i]}_label`, + 'labels' + ) + div.style.fontSize = NODE_LABEL_DEFAULT_SIZE + div.style.display = 'none' + nodeLabelsDivs.push(div) + }) + + for (const layer of ctx.layers) { + const div = createLabel( + container, + layer.getName(), + `${layer.getName()}_label`, + 'layer-labels' + ) + div.style.display = 'inline-block' + layerLabelsDivs.push(div) + } + ctx.renderLayerLabelsFlag = true +} + +// v2 setLabelColorVariable + setLabelColor. +export function setLabelColor(color = ctx.labelColor): void { + ctx.labelColor = color + for (const div of layerLabelsDivs) div.style.color = color + for (const div of nodeLabelsDivs) div.style.color = color +} + +// Handlers (v2 Shiny handlers) ===== + +export function showLayerLabels(mode: 'all' | 'selected' | 'none'): void { + ctx.showAllLayerLabelsFlag = mode === 'all' + ctx.showSelectedLayerLabelsFlag = mode === 'selected' + + layerLabelsDivs.forEach((div, i) => { + const visible = + mode === 'all' || (mode === 'selected' && ctx.layers[i].isSelected) + div.style.display = visible ? 'inline-block' : 'none' + }) + if (mode !== 'none') ctx.renderLayerLabelsFlag = true +} + +export function showNodeLabels(mode: 'all' | 'selected' | 'none'): void { + ctx.showAllNodeLabelsFlag = mode === 'all' + ctx.showSelectedNodeLabelsFlag = mode === 'selected' + decideNodeLabelFlags() +} + +export function resizeLayerLabels(size: number): void { + for (const div of layerLabelsDivs) div.style.fontSize = `${size}px` +} + +export function resizeNodeLabels(size: number): void { + for (const div of nodeLabelsDivs) div.style.fontSize = `${size}px` +} + +// Animate-loop rendering ===== + +export function renderLayerLabels(): void { + if (!ctx.renderLayerLabelsFlag) return + + if (ctx.showAllLayerLabelsFlag) { + redrawLayerLabels(ctx.layers.map((l) => l.id)) + } else if (ctx.showSelectedLayerLabelsFlag) { + for (const div of layerLabelsDivs) div.style.display = 'none' + const selected = ctx.layers.filter((l) => l.isSelected).map((l) => l.id) + if (selected.length > 0) redrawLayerLabels(selected) + } + ctx.renderLayerLabelsFlag = false +} + +// v2 read the hideLayer DOM checkboxes; hidden state lives on Layer now. +function redrawLayerLabels(indexes: number[]): void { + for (const i of indexes) { + const div = layerLabelsDivs[i] + if (!div) continue + if (ctx.layers[i].isVisible) { + const world = ctx.layers[i].sphere.getWorldPosition(VEC3.set(0, 0, 0)) + div.style.left = `${ctx.xBoundMax + world.x}px` + div.style.top = `${ctx.yBoundMax - world.y}px` + div.style.display = 'inline-block' + } else div.style.display = 'none' + } +} + +export function renderNodeLabels(): void { + if (!ctx.renderNodeLabelsFlag) return + + ctx.nodeObjects.forEach((node, i) => { + const div = nodeLabelsDivs[i] + if (!div) return + if (node.showLabel) { + div.style.left = `${ctx.xBoundMax + node.getWorldPosition('x') + 7}px` + div.style.top = `${ctx.yBoundMax - node.getWorldPosition('y') - 10}px` + div.style.display = 'inline-block' + } else div.style.display = 'none' + }) + ctx.renderNodeLabelsFlag = false +} + +// Called once from main.ts. +export function registerLabelRendering(): void { + registerAnimateHook(renderLayerLabels) + registerAnimateHook(renderNodeLabels) +} diff --git a/frontend/src/actions/network.ts b/frontend/src/actions/network.ts index 428712a..1ee37be 100644 --- a/frontend/src/actions/network.ts +++ b/frontend/src/actions/network.ts @@ -17,6 +17,7 @@ import { resetContext, Scene, } from '../three' +import { createLabels } from './labels' import { initialSpreadLayers } from './layer' import { scrambleNodes } from './node' @@ -46,6 +47,7 @@ export function buildNetwork(data: NetworkData): void { initializeChannels(data.channels) initializeNodes(data.nodes) createEdgeObjects(data.edges) + createLabels() // v2 executePostNetworkSetup store.update({ network: data, selectedChannels: [...data.channels] }) } diff --git a/frontend/src/actions/node.ts b/frontend/src/actions/node.ts index 3989065..80d4237 100644 --- a/frontend/src/actions/node.ts +++ b/frontend/src/actions/node.ts @@ -66,6 +66,7 @@ export function checkHoverOverNode(event: { // v2 read the hideLayer DOM checkboxes; layer visibility now lives on Layer. export function decideNodeLabelFlags(): void { + ctx.renderNodeLabelsFlag = true ctx.nodeObjects.forEach((node, i) => { const layer = ctx.layers[ctx.layerGroups[ctx.nodeGroups[ctx.nodeLayerNames[i]]]] diff --git a/frontend/src/actions/screen.ts b/frontend/src/actions/screen.ts index 1e6ec24..7647e55 100644 --- a/frontend/src/actions/screen.ts +++ b/frontend/src/actions/screen.ts @@ -71,13 +71,19 @@ export function raycaster(): THREE.Raycaster { return RAYCASTER } +// Per-tick hooks (labels.ts registers its render fns here; avoids an +// import cycle screen -> labels -> node -> screen). +const animateHooks: Array<() => void> = [] +export function registerAnimateHook(fn: () => void): void { + animateHooks.push(fn) +} + // FPS-limited render loop. export function animate(): void { setTimeout(() => requestAnimationFrame(animate), 1000 / ctx.fps) - // ponytail: CSS2D label rendering hooks in here once labels.ts lands (v2 - // called renderLayerLabels / renderNodeLabels before render). renderInterLayerEdges() + for (const fn of animateHooks) fn() if (ctx.scene && ctx.camera) ctx.renderer?.render(ctx.scene.THREE_Object, ctx.camera) } diff --git a/frontend/src/actions/themes.ts b/frontend/src/actions/themes.ts index 0ea8984..2084edc 100644 --- a/frontend/src/actions/themes.ts +++ b/frontend/src/actions/themes.ts @@ -7,6 +7,7 @@ import { bus } from '../bus' import { store } from '../store' import { ctx } from '../three' import { assignChannelColorsFromPalette, redrawIntraLayerEdges } from './edge' +import { setLabelColor } from './labels' import { repaintLayers } from './layer' import { setRendererColor } from './screen' @@ -65,8 +66,8 @@ export function applyTheme(name: string, fromInit = false): void { repaintLayers(theme.floor) redrawIntraLayerEdges() ctx.renderInterLayerEdgesFlag = true - // v2 also rebuilt the channel edit list + label colors — Phase 13 UI / - // labels.ts react to the same theme:changed event + setLabelColor(theme.label) + // v2 also rebuilt the channel edit list — Phase 13 UI } } diff --git a/frontend/src/main.ts b/frontend/src/main.ts index be709f1..fa83dd0 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -6,6 +6,7 @@ import { fetchConfig } from './api/config' import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' +import { registerLabelRendering } from './actions/labels' import { setRenderer, resetScreen, animate } from './actions/screen' import { registerThemeListener } from './actions/themes' import { registerGlobalListeners } from './event_listeners' @@ -36,6 +37,7 @@ async function main(): Promise { registerGlobalListeners() registerThemeListener() + registerLabelRendering() animate() // Test hook: lets Playwright read scene state via page.evaluate (WebGL is diff --git a/frontend/src/three/runtime.ts b/frontend/src/three/runtime.ts index 283b499..abaff70 100644 --- a/frontend/src/three/runtime.ts +++ b/frontend/src/three/runtime.ts @@ -70,6 +70,9 @@ export interface RuntimeContext { selectedNodeColorFlag: boolean showAllNodeLabelsFlag: boolean showSelectedNodeLabelsFlag: boolean + renderNodeLabelsFlag: boolean + showAllLayerLabelsFlag: boolean + showSelectedLayerLabelsFlag: boolean // Inter-layer edge render machinery (v2 edge.js globals) renderInterLayerEdgesFlag: boolean @@ -133,6 +136,9 @@ export const ctx: RuntimeContext = { selectedNodeColorFlag: true, showAllNodeLabelsFlag: false, showSelectedNodeLabelsFlag: true, + renderNodeLabelsFlag: false, + showAllLayerLabelsFlag: true, + showSelectedLayerLabelsFlag: false, renderInterLayerEdgesFlag: false, waitEdgeRenderFlag: true, @@ -176,6 +182,9 @@ export function resetContext(): void { ctx.selectedNodeColorFlag = true ctx.showAllNodeLabelsFlag = false ctx.showSelectedNodeLabelsFlag = true + ctx.renderNodeLabelsFlag = false + ctx.showAllLayerLabelsFlag = true + ctx.showSelectedLayerLabelsFlag = false ctx.renderInterLayerEdgesFlag = false ctx.waitEdgeRenderFlag = true ctx.interEdgesRemoved = false From 8b3a7c8c392e31ff14d76eabcab76343a78c67d3 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 10:31:57 +0100 Subject: [PATCH 026/166] frontend: canvas mouse/keyboard scene controls (Phase 12 partial) Port of the canvas half of www/js/event_listeners.js plus the held-key and lasso helpers it pulled from node.js/layer.js: wheel zoom, z/x/c axis keys + arrow-key pan (modern event.key), clickDown/Drag/Up with left-drag pan, middle-drag orbit, held-key node-translate/layer-rotate, shift-lasso rectangle select (dim to 0.5, select on mouse-up), and dblClick with v2's node -> layer -> unselect-all priority. registerCanvasControls() wires the listeners onto the mounted canvas from main.ts (tabIndex for keydown, scroll/autoscroll prevention, mouseleave button release). Shiny syncs and 100ms debounces dropped. Right-click node menu stays with right_click_menu.ts; DragControls layer dragging and the nav-button/slider table with Phase 13. Co-Authored-By: Claude Fable 5 --- PLAN.md | 4 +- frontend/src/actions/canvas_controls.test.ts | 129 ++++++++++ frontend/src/actions/canvas_controls.ts | 241 +++++++++++++++++++ frontend/src/actions/node.ts | 2 +- frontend/src/main.ts | 2 + 5 files changed, 375 insertions(+), 3 deletions(-) create mode 100644 frontend/src/actions/canvas_controls.test.ts create mode 100644 frontend/src/actions/canvas_controls.ts diff --git a/PLAN.md b/PLAN.md index 185dc7f..0dccfd1 100644 --- a/PLAN.md +++ b/PLAN.md @@ -153,7 +153,7 @@ Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2 - [x] `layer.js` → `src/actions/layer.ts` — `initialSpreadLayers` (moved from network.ts), hover (`checkHoverOverLayer` red-highlight + repaint), double-click/`selectLayer`/select-all selection (store-synced), `repaintLayers` (selected → default/picker priority), per-layer visibility + node-label toggles (recompute node label flags), coords/opacity/wireframe/color-priority setters. New ctx state: `lastHoveredLayerIndex`, `hoveredLayerPaintedFlag`, `renderLayerLabelsFlag`. 6 Vitest tests. *(Checkbox group + picker DOM → Phase 13; DragControls + held-key/slider rotate/move/scale intervals → canvas_controls.ts.)* - [x] `themes.js` → `src/actions/themes.ts` — `THEMES` presets (light/dark/gray from v2 buttons), `applyTheme(name, fromInit)` (renderer clear color, `ctx.edgeDefaultColor`/`labelColor`, channel palette from config, picker-priority layer repaint with theme floor color), `registerThemeListener()` wired to `theme:changed` in main.ts so `ChangeThemeCommand` execute/undo recolors. Added `ctx.labelColor` (v2 `globalLabelColor`), `repaintLayers(pickerColor?)`, `assignChannelColorsFromPalette` in edge.ts. 4 Vitest tests. *(Theme buttons DOM + channel edit list rebuild → Phase 13; label recolor → labels.ts.)* - [x] `labels.js` → `src/actions/labels.ts` — v2's plain absolutely-positioned divs in `#labelDiv` (kept over CSS2DRenderer: no new dep, inline `position:absolute` until the Phase 13 stylesheet), `createLabels` on network build, flag-gated `renderLayer/NodeLabels` registered as animate hooks (`registerAnimateHook` added to screen.ts — avoids screen→labels→node→screen cycle), `showLayer/NodeLabels` modes, resize + `setLabelColor` (themes now recolors labels). Added `ctx.renderNodeLabelsFlag` + layer-label flags; `decideNodeLabelFlags` raises the render flag as v2 did. 4 Vitest tests (flag machinery; div positioning → Phase 13 Playwright). `index.html` gains `#labelDiv`. -- [ ] `canvas_controls.js` → `src/actions/canvas_controls.ts` (mouse/keyboard canvas controls, 510 lines, heavy DOM) +- [x] `canvas_controls.js` (core) + canvas half of `event_listeners.js` → `src/actions/canvas_controls.ts` — `sceneZoom`, `keyPressed`/`axisRelease` (modern `event.key`), `clickDown/Drag/Up`, `dblClick` (node → layer → unselect-all priority), scene pan/orbit, held-key node-translate/layer-rotate (the node.js/layer.js deferrals), shift-lasso select (anchor, THREE.Line rectangle, dim-then-select), `registerCanvasControls()` on the canvas from main.ts (tabIndex, wheel/scroll prevention, mouseleave release). 8 Vitest tests. *(Right-click node menu → right_click_menu.ts; DragControls layer dragging still open; nav buttons/slider control table DOM → Phase 13.)* - [ ] `right_click_menu.js` → `src/actions/right_click_menu.ts` (DOM context menu) - [ ] Delete `www/js/object_actions/` @@ -167,7 +167,7 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Migrate `screen.js` → `src/actions/screen.ts` — renderer, camera, window bounds, raycaster, FPS-limited `animate()`. Added `ctx.renderer/camera/fps/*Bound*` (persist across network reloads, not in `resetContext`). - [x] Migrate `event_listeners.js` (window-level) → `src/event_listeners.ts` — `resize` + `Ctrl+Z` / `Ctrl+Shift+Z` (and `Ctrl+Y`) undo/redo wired to `CommandHistory` - [x] Migrate `on_page_load.js` → `src/main.ts` — config fetch → renderer/camera/scene setup → mount canvas → animate; exposes `window.__arena = { ctx, history }` test hook (Phase 13 Playwright reads scene state through it) -- [ ] **Deferred (need canvas_controls, still in Phase 11 list):** the canvas mouse/keyboard scene controls (`clickDown/Drag/Up`, `sceneZoom`, `keyPressed`, right-click menu) — land with `canvas_controls.ts`. +- [x] **Canvas mouse/keyboard scene controls** (`clickDown/Drag/Up`, `sceneZoom`, `keyPressed`) — landed in `canvas_controls.ts` (see Phase 11 list). Right-click menu still open (`right_click_menu.ts`). - [ ] Replace `rshiny_handlers.js` + `rshiny_update.js` fully with EventBus + API (foundation done: bus/store/api client + commands already dispatch events; remaining handlers map to the Phase 11-deferred actions + Phase 13 panels) - [ ] Delete `www/js/event_listeners.js`, `www/js/on_page_load.js`; `www/js/general.js` → **can delete now** (fully ported to `src/utils.ts`) - [ ] Delete `www/js/rshiny_handlers.js`, `www/js/rshiny_update.js` diff --git a/frontend/src/actions/canvas_controls.test.ts b/frontend/src/actions/canvas_controls.test.ts new file mode 100644 index 0000000..c6766c4 --- /dev/null +++ b/frontend/src/actions/canvas_controls.test.ts @@ -0,0 +1,129 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { ctx, Layer, Node, resetContext, Scene } from '../three' +import { + axisRelease, + clickDown, + clickDrag, + clickUp, + dblClick, + keyPressed, + lassoSelectNodes, + rotateLayersWithHeldKey, + sceneZoom, +} from './canvas_controls' + +type AnyEvent = Parameters[0] + +function fakeEvent(partial: Partial): AnyEvent { + return { + button: 0, + shiftKey: false, + screenX: 0, + screenY: 0, + layerX: 0, + layerY: 0, + preventDefault: () => {}, + ...partial, + } as AnyEvent +} + +beforeEach(() => { + resetContext() + ctx.scene = new Scene() + ctx.layers = [new Layer({ id: 0, name: 'L1' })] + ctx.layerGroups = { L1: 0 } + ctx.nodeGroups = { A_L1: 'L1', B_L1: 'L1' } + ctx.nodeLayerNames = ['A_L1', 'B_L1'] + ctx.nodeObjects = [ + new Node({ id: 0, name: 'A', nodeLayerName: 'A_L1', layer: 'L1' }), + new Node({ + id: 1, + name: 'B', + nodeLayerName: 'B_L1', + layer: 'L1', + position_x: 50, + position_y: 50, + }), + ] +}) + +describe('keyboard', () => { + it('z/x/c set the pressed axis; keyup releases it', () => { + keyPressed({ key: 'z', preventDefault: () => {} } as KeyboardEvent) + expect(ctx.scene!.axisPressed).toBe('z') + axisRelease() + expect(ctx.scene!.axisPressed).toBe('') + }) + + it('arrow keys pan the scene', () => { + const spy = vi.spyOn(ctx.scene!, 'translatePanWithArrow') + keyPressed({ + key: 'ArrowRight', + preventDefault: () => {}, + } as KeyboardEvent) + expect(spy).toHaveBeenCalledWith(39) + }) +}) + +describe('zoom', () => { + it('wheel scroll scales the scene', () => { + const before = ctx.scene!.getScale() + sceneZoom({ deltaY: -1, preventDefault: () => {} } as WheelEvent) + expect(ctx.scene!.getScale()).toBeCloseTo(before * 1.1) + }) +}) + +describe('drag', () => { + it('left-drag with nothing hovered pans the scene', () => { + const spy = vi.spyOn(ctx.scene!, 'translatePanWithMouse') + clickDown(fakeEvent({ button: 0 })) + clickDrag(fakeEvent({ screenX: 50, screenY: 0 })) + expect(ctx.scene!.dragging).toBe(true) + expect(spy).toHaveBeenCalledWith(50, 0) + expect(ctx.mousePreviousX).toBe(50) + }) + + it('middle-drag orbits the scene sphere', () => { + const spy = vi.spyOn(ctx.scene!, 'orbitSphereWithMouse') + clickDown(fakeEvent({ button: 1 })) + clickDrag(fakeEvent({ screenX: 0, screenY: 50 })) + expect(spy).toHaveBeenCalled() + }) + + it('held axis key rotates selected layers instead of panning', () => { + ctx.layers[0].isSelected = true + ctx.scene!.axisPressed = 'z' + const before = ctx.layers[0].getRotation('z') + rotateLayersWithHeldKey({ screenX: 50, screenY: 0 }) + expect(ctx.layers[0].getRotation('z')).toBeCloseTo(before + 0.05) + }) +}) + +describe('lasso', () => { + it('shift-click anchors, drag dims inside nodes, mouse-up selects them', () => { + // anchor at (-5, 5): layerX = xBoundMax - 5, layerY = yBoundMax - 5 + clickDown( + fakeEvent({ + shiftKey: true, + layerX: ctx.xBoundMax - 5, + layerY: ctx.yBoundMax - 5, + }) + ) + lassoSelectNodes(5, -5) // rectangle (-5..5) x (-5..5) + expect(ctx.nodeObjects[0].getOpacity()).toBe(0.5) // A at origin: inside + expect(ctx.nodeObjects[1].getOpacity()).toBe(1) // B at (50,50): outside + + clickUp(fakeEvent({}) as unknown as MouseEvent) + expect(ctx.nodeObjects[0].isSelected).toBe(true) + expect(ctx.nodeObjects[1].isSelected).toBe(false) + expect(ctx.scene!.dragging).toBe(false) + }) +}) + +describe('dblClick', () => { + it('with nothing hovered unselects all nodes', () => { + ctx.nodeObjects[0].isSelected = true + dblClick(fakeEvent({ clientX: 0, clientY: 0 } as Partial)) + expect(ctx.nodeObjects[0].isSelected).toBe(false) + }) +}) diff --git a/frontend/src/actions/canvas_controls.ts b/frontend/src/actions/canvas_controls.ts new file mode 100644 index 0000000..37a9ae0 --- /dev/null +++ b/frontend/src/actions/canvas_controls.ts @@ -0,0 +1,241 @@ +// Canvas mouse/keyboard scene controls — port of the canvas half of v2 +// www/js/event_listeners.js plus the held-key/lasso helpers it pulled from +// node.js/layer.js. Shiny syncs and the 100ms update debounces dropped. +// Deferred: right-click node menu (right_click_menu.ts), DragControls layer +// dragging, nav-button/slider control table (Phase 13 UI). + +import * as THREE from 'three' +import { ctx } from '../three' +import { redrawIntraLayerEdges, unselectAllEdges } from './edge' +import { + checkHoverOverLayer, + getSelectedLayers, + performDoubleClickLayerSelection, +} from './layer' +import { + checkHoverOverNode, + decideNodeLabelFlags, + getSelectedNodes, + performDoubleClickNodeSelection, + repaintNode, + unselectAllNodes, + updateSelectedNodesStore, +} from './node' + +// v2 globals: lasso anchor (shift+click) and the lasso rectangle line. +let shiftX: number | null = null +let shiftY: number | null = null +let lasso: THREE.Line | null = null + +type CanvasMouseEvent = MouseEvent & { layerX: number; layerY: number } + +// on mouse wheel scroll +export function sceneZoom(event: WheelEvent): void { + if (!ctx.scene?.exists()) return + event.preventDefault() // keep the page from scrolling (v2 initializeCanvasDiv) + ctx.scene.zoom(event.deltaY) +} + +const ARROW_CODES: Record = { + ArrowLeft: 37, + ArrowUp: 38, + ArrowRight: 39, + ArrowDown: 40, +} + +// on z/x/c axis select (held-key layer/node transforms) or arrow-key pan +export function keyPressed(event: KeyboardEvent): void { + if (!ctx.scene?.exists()) return + const key = event.key.toLowerCase() + if (key === 'z' || key === 'x' || key === 'c') ctx.scene.axisPressed = key + else if (event.key in ARROW_CODES) { + event.preventDefault() + ctx.scene.translatePanWithArrow(ARROW_CODES[event.key]) + } +} + +export function axisRelease(): void { + if (ctx.scene?.exists()) ctx.scene.axisPressed = '' +} + +// mouse: 0 left, 1 middle, 2 right click +export function clickDown(event: CanvasMouseEvent): void { + if (!ctx.scene?.exists()) return + if (event.button === 0) { + ctx.scene.leftClickPressed = true + ctx.scene.middleClickPressed = false + if (event.shiftKey && shiftX === null) { + shiftX = event.layerX - ctx.xBoundMax + shiftY = ctx.yBoundMax - event.layerY // used in drag + } + } else if (event.button === 1) { + event.preventDefault() // no middle-click autoscroll + ctx.scene.middleClickPressed = true + ctx.scene.leftClickPressed = false + } else { + ctx.scene.middleClickPressed = false + ctx.scene.leftClickPressed = false + } +} + +// while mouse button held, drag event +export function clickDrag(event: CanvasMouseEvent): void { + if (!ctx.scene?.exists()) return + const distance = Math.hypot( + ctx.mousePreviousX - event.screenX, + ctx.mousePreviousY - event.screenY + ) + + if (distance > 10) { + const x = event.screenX + const y = event.screenY + + if (ctx.scene.leftClickPressed) { + ctx.scene.dragging = true + if (event.shiftKey) { + ctx.lastHoveredLayerIndex = null // to be able to lasso inside a layer + lassoSelectNodes( + event.layerX - ctx.xBoundMax, + ctx.yBoundMax - event.layerY + ) + } else if (ctx.scene.axisPressed !== '' && getSelectedNodes().length > 0) + translateNodesWithHeldKey(event) + else if (ctx.scene.axisPressed !== '') rotateLayersWithHeldKey(event) + else if ( + ctx.lastHoveredLayerIndex === null && + ctx.lastHoveredNodeIndex === null + ) + ctx.scene.translatePanWithMouse(x, y) + } else if (ctx.scene.middleClickPressed) { + ctx.scene.dragging = true + event.preventDefault() + ctx.scene.orbitSphereWithMouse(x, y) + } + + ctx.mousePreviousX = x + ctx.mousePreviousY = y + } + + if (!ctx.scene.leftClickPressed && !ctx.scene.middleClickPressed) + if (!checkHoverOverNode(event)) checkHoverOverLayer(event) +} + +export function clickUp(event: MouseEvent): void { + if (!ctx.scene?.exists()) return + ctx.scene.dragging = false + if (event.button === 0) { + ctx.scene.leftClickPressed = false + if (lasso) { + // nodes dimmed to 0.5 are inside the lasso -> select them + ctx.nodeObjects.forEach((node, i) => { + if (node.getOpacity() === 0.5) { + node.setOpacity(1) + node.isSelected = true + repaintNode(i) + } + }) + decideNodeLabelFlags() + updateSelectedNodesStore() + ctx.scene.remove(lasso) + lasso = null + } + shiftX = null + shiftY = null + } else if (event.button === 1) ctx.scene.middleClickPressed = false +} + +// double click: select node -> select layer -> unselect everything +export function dblClick(event: CanvasMouseEvent): void { + if (!ctx.scene?.exists()) return + if (performDoubleClickNodeSelection(event)) return + if (ctx.lastHoveredLayerIndex !== null) performDoubleClickLayerSelection() + else { + unselectAllNodes() + unselectAllEdges() + redrawIntraLayerEdges() + } +} + +// v2 node.js translateNodesWithHeldKey — z/c + drag moves selected nodes. +export function translateNodesWithHeldKey(event: { + screenX: number + screenY: number +}): void { + const step = + event.screenX - event.screenY >= ctx.mousePreviousX - ctx.mousePreviousY + ? 20 + : -20 + + for (const i of getSelectedNodes()) { + if (ctx.scene!.axisPressed === 'z') ctx.nodeObjects[i].translateZ(step) + else if (ctx.scene!.axisPressed === 'c') ctx.nodeObjects[i].translateY(step) + } + redrawIntraLayerEdges() +} + +// v2 layer.js rotateLayersWithHeldKey — z/x/c + drag rotates selected layers. +export function rotateLayersWithHeldKey(event: { + screenX: number + screenY: number +}): void { + const rads = + event.screenX - event.screenY >= ctx.mousePreviousX - ctx.mousePreviousY + ? 0.05 + : -0.05 + + for (const i of getSelectedLayers()) { + if (ctx.scene!.axisPressed === 'z') ctx.layers[i].rotateZ(rads) + else if (ctx.scene!.axisPressed === 'x') ctx.layers[i].rotateX(rads) + else if (ctx.scene!.axisPressed === 'c') ctx.layers[i].rotateY(rads) + } +} + +// v2 node.js lassoSelectNodes — shift + left-drag rectangle select. +export function lassoSelectNodes(x: number, y: number): void { + if (shiftX === null || shiftY === null) return + const minX = Math.min(shiftX, x) + const maxX = Math.max(shiftX, x) + const minY = Math.min(shiftY, y) + const maxY = Math.max(shiftY, y) + + createLassoGeometry(x, y) + + for (const node of ctx.nodeObjects) { + const nodeX = node.getWorldPosition('x') + const nodeY = node.getWorldPosition('y') + node.setOpacity( + nodeX < maxX && nodeX > minX && nodeY < maxY && nodeY > minY ? 0.5 : 1 + ) + } +} + +function createLassoGeometry(x: number, y: number): void { + if (lasso) ctx.scene!.remove(lasso) + const points = [ + new THREE.Vector3(shiftX!, shiftY!, 0), + new THREE.Vector3(x, shiftY!, 0), + new THREE.Vector3(x, y, 0), + new THREE.Vector3(shiftX!, y, 0), + new THREE.Vector3(shiftX!, shiftY!, 0), + ] + lasso = new THREE.Line( + new THREE.BufferGeometry().setFromPoints(points), + new THREE.LineBasicMaterial({ color: '#eef1b6' }) + ) + ctx.scene!.add(lasso) +} + +// Called once from main.ts, after the canvas is mounted. +export function registerCanvasControls(): void { + const canvas = ctx.renderer?.domElement + if (!canvas) return + canvas.tabIndex = 1 // focusable, so it receives keydown events (v2) + canvas.addEventListener('wheel', sceneZoom) + canvas.addEventListener('keydown', keyPressed) + canvas.addEventListener('keyup', axisRelease) + canvas.addEventListener('mousedown', clickDown as EventListener) + canvas.addEventListener('mousemove', clickDrag as EventListener) + canvas.addEventListener('mouseup', clickUp) + canvas.addEventListener('dblclick', dblClick as EventListener) + canvas.addEventListener('mouseleave', clickUp) // release buttons on exit (v2) +} diff --git a/frontend/src/actions/node.ts b/frontend/src/actions/node.ts index 80d4237..6a535fe 100644 --- a/frontend/src/actions/node.ts +++ b/frontend/src/actions/node.ts @@ -27,7 +27,7 @@ export function scrambleNodes( } } -function updateSelectedNodesStore(): void { +export function updateSelectedNodesStore(): void { store.update({ selectedNodes: ctx.nodeObjects .filter((n) => n.isSelected) diff --git a/frontend/src/main.ts b/frontend/src/main.ts index fa83dd0..79c0caa 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -6,6 +6,7 @@ import { fetchConfig } from './api/config' import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' +import { registerCanvasControls } from './actions/canvas_controls' import { registerLabelRendering } from './actions/labels' import { setRenderer, resetScreen, animate } from './actions/screen' import { registerThemeListener } from './actions/themes' @@ -38,6 +39,7 @@ async function main(): Promise { registerGlobalListeners() registerThemeListener() registerLabelRendering() + registerCanvasControls() animate() // Test hook: lets Playwright read scene state via page.evaluate (WebGL is From 85a272820e537030a207efdc465c4a01c86c3d85 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 10:35:05 +0100 Subject: [PATCH 027/166] frontend: right-click node menu + path-selection commands (Phase 12 partial) Port of www/js/object_actions/right_click_menu.js plus the context-menu builder from event_listeners.js: selectNeighbors, selectMultiLayerPath (v2 starting/current-layer exclusions), recursive selectDownstreamPath over inter-layer edges, and the executeCommand tail (render flags, label flags, store sync). The ` menu over the clicked node in `#labelDiv` (Link opens URL; Description → Phase 13 panel), wired to `contextmenu` + removed on `clickUp` in canvas_controls. 5 Vitest tests. *(Loader spinner + descrDiv content → Phase 13.)* - [ ] Delete `www/js/object_actions/` --- @@ -167,7 +167,7 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Migrate `screen.js` → `src/actions/screen.ts` — renderer, camera, window bounds, raycaster, FPS-limited `animate()`. Added `ctx.renderer/camera/fps/*Bound*` (persist across network reloads, not in `resetContext`). - [x] Migrate `event_listeners.js` (window-level) → `src/event_listeners.ts` — `resize` + `Ctrl+Z` / `Ctrl+Shift+Z` (and `Ctrl+Y`) undo/redo wired to `CommandHistory` - [x] Migrate `on_page_load.js` → `src/main.ts` — config fetch → renderer/camera/scene setup → mount canvas → animate; exposes `window.__arena = { ctx, history }` test hook (Phase 13 Playwright reads scene state through it) -- [x] **Canvas mouse/keyboard scene controls** (`clickDown/Drag/Up`, `sceneZoom`, `keyPressed`) — landed in `canvas_controls.ts` (see Phase 11 list). Right-click menu still open (`right_click_menu.ts`). +- [x] **Canvas mouse/keyboard scene controls** (`clickDown/Drag/Up`, `sceneZoom`, `keyPressed`, right-click menu) — landed in `canvas_controls.ts` + `right_click_menu.ts` (see Phase 11 list). - [ ] Replace `rshiny_handlers.js` + `rshiny_update.js` fully with EventBus + API (foundation done: bus/store/api client + commands already dispatch events; remaining handlers map to the Phase 11-deferred actions + Phase 13 panels) - [ ] Delete `www/js/event_listeners.js`, `www/js/on_page_load.js`; `www/js/general.js` → **can delete now** (fully ported to `src/utils.ts`) - [ ] Delete `www/js/rshiny_handlers.js`, `www/js/rshiny_update.js` diff --git a/frontend/src/actions/canvas_controls.ts b/frontend/src/actions/canvas_controls.ts index 37a9ae0..2690324 100644 --- a/frontend/src/actions/canvas_controls.ts +++ b/frontend/src/actions/canvas_controls.ts @@ -1,8 +1,8 @@ // Canvas mouse/keyboard scene controls — port of the canvas half of v2 // www/js/event_listeners.js plus the held-key/lasso helpers it pulled from // node.js/layer.js. Shiny syncs and the 100ms update debounces dropped. -// Deferred: right-click node menu (right_click_menu.ts), DragControls layer -// dragging, nav-button/slider control table (Phase 13 UI). +// Deferred: DragControls layer dragging, nav-button/slider control table +// (Phase 13 UI). import * as THREE from 'three' import { ctx } from '../three' @@ -21,6 +21,10 @@ import { unselectAllNodes, updateSelectedNodesStore, } from './node' +import { + removeContextMenu, + replaceContextMenuOverNode, +} from './right_click_menu' // v2 globals: lasso anchor (shift+click) and the lasso rectangle line. let shiftX: number | null = null @@ -125,6 +129,7 @@ export function clickUp(event: MouseEvent): void { ctx.scene.dragging = false if (event.button === 0) { ctx.scene.leftClickPressed = false + removeContextMenu() // v2 removed the right-click options list here if (lasso) { // nodes dimmed to 0.5 are inside the lasso -> select them ctx.nodeObjects.forEach((node, i) => { @@ -238,4 +243,8 @@ export function registerCanvasControls(): void { canvas.addEventListener('mouseup', clickUp) canvas.addEventListener('dblclick', dblClick as EventListener) canvas.addEventListener('mouseleave', clickUp) // release buttons on exit (v2) + canvas.addEventListener( + 'contextmenu', + replaceContextMenuOverNode as unknown as EventListener + ) } diff --git a/frontend/src/actions/right_click_menu.test.ts b/frontend/src/actions/right_click_menu.test.ts new file mode 100644 index 0000000..e6fa716 --- /dev/null +++ b/frontend/src/actions/right_click_menu.test.ts @@ -0,0 +1,104 @@ +import { beforeEach, describe, expect, it } from 'vitest' +import { store } from '../store' +import { ctx, Edge, Layer, Node, resetContext, Scene } from '../three' +import { + executeCommand, + selectDownstreamPath, + selectMultiLayerPath, + selectNeighbors, +} from './right_click_menu' + +// Three layers; A,D on L1, B on L2, C on L3. +// Edges: A-B (inter), B-C (inter), A-D (intra L1). +function seed(): void { + ctx.scene = new Scene() + ctx.layers = [ + new Layer({ id: 0, name: 'L1' }), + new Layer({ id: 1, name: 'L2' }), + new Layer({ id: 2, name: 'L3' }), + ] + ctx.layerGroups = { L1: 0, L2: 1, L3: 2 } + ctx.nodeGroups = { A_L1: 'L1', B_L2: 'L2', C_L3: 'L3', D_L1: 'L1' } + ctx.nodeLayerNames = ['A_L1', 'B_L2', 'C_L3', 'D_L1'] + ctx.nodeObjects = ctx.nodeLayerNames.map( + (nl, i) => + new Node({ + id: i, + name: nl.split('_')[0], + nodeLayerName: nl, + layer: ctx.nodeGroups[nl], + }) + ) + ctx.nodeObjects.forEach((n, i) => + ctx.layers[ctx.layerGroups[ctx.nodeGroups[ctx.nodeLayerNames[i]]]].addNode( + n.sphere + ) + ) + ctx.edgeObjects = [ + new Edge({ + id: 0, + source: 'A_L1', + target: 'B_L2', + weights: [1], + interLayer: true, + }), + new Edge({ + id: 1, + source: 'B_L2', + target: 'C_L3', + weights: [1], + interLayer: true, + }), + new Edge({ id: 2, source: 'A_L1', target: 'D_L1', weights: [1] }), + ] +} + +const selectedNodes = (): number[] => + ctx.nodeObjects.filter((n) => n.isSelected).map((n) => n.id) + +beforeEach(() => { + resetContext() + store.update({ selectedNodes: [] }) + seed() +}) + +describe('selectNeighbors', () => { + it('selects direct neighbors and their edges (inter and intra)', () => { + selectNeighbors(0) // A + expect(selectedNodes()).toEqual([1, 3]) // B and D + expect(ctx.edgeObjects[0].isSelected).toBe(true) + expect(ctx.edgeObjects[2].isSelected).toBe(true) + expect(ctx.edgeObjects[1].isSelected).toBe(false) + }) +}) + +describe('selectMultiLayerPath', () => { + it('walks across layers, skipping the starting and current layer', () => { + selectMultiLayerPath(0) // A: -> B (L2) -> C (L3); D (L1) excluded + expect(selectedNodes()).toEqual([1, 2]) + }) +}) + +describe('selectDownstreamPath', () => { + it('follows inter-layer edges into unvisited layers only', () => { + selectDownstreamPath(0) // A -> B -> C; D shares the starting layer + expect(selectedNodes()).toEqual([1, 2]) + expect(ctx.edgeObjects[0].isSelected).toBe(true) + expect(ctx.edgeObjects[1].isSelected).toBe(true) + expect(ctx.edgeObjects[2].isSelected).toBe(false) + }) +}) + +describe('executeCommand', () => { + it('runs the command, raises render flags and syncs the store', () => { + executeCommand(0, 'Select Neighbors') + expect(ctx.renderInterLayerEdgesFlag).toBe(true) + expect(store.get().selectedNodes).toEqual(['B_L2', 'D_L1']) + }) + + it('"-" is a no-op', () => { + executeCommand(0, '-') + expect(selectedNodes()).toEqual([]) + expect(ctx.renderInterLayerEdgesFlag).toBe(false) + }) +}) diff --git a/frontend/src/actions/right_click_menu.ts b/frontend/src/actions/right_click_menu.ts new file mode 100644 index 0000000..5d99fc9 --- /dev/null +++ b/frontend/src/actions/right_click_menu.ts @@ -0,0 +1,218 @@ +// Port of v2 www/js/object_actions/right_click_menu.js + the context-menu +// builder from event_listeners.js. The graph commands (neighbors, multilayer +// path, downstream path) are pure; the menu is a over it (v2 +// replaceContextMenuOverNode from event_listeners.js). +export function replaceContextMenuOverNode(event: { + layerX: number + layerY: number + preventDefault: () => void +}): void { + removeContextMenu() + const container = + typeof document === 'undefined' ? null : document.getElementById('labelDiv') + if (!container) return + + for (let i = 0; i < ctx.nodeObjects.length; i++) { + const nodeX = ctx.xBoundMax + ctx.nodeObjects[i].getWorldPosition('x') + const nodeY = ctx.yBoundMax - ctx.nodeObjects[i].getWorldPosition('y') + if ( + (nodeX - event.layerX) ** 2 + (nodeY - event.layerY) ** 2 > + (SPHERE_RADIUS + 1) ** 2 + ) + continue + + event.preventDefault() + optionsList = buildMenu(i, nodeX, nodeY) + container.appendChild(optionsList) + return + } +} + +function buildMenu(nodeIndex: number, x: number, y: number): HTMLSelectElement { + const node = ctx.nodeObjects[nodeIndex] + const select = document.createElement('select') + select.className = 'optionsBox' + select.style.position = 'absolute' + select.style.left = `${x}px` + select.style.top = `${y}px` + select.style.display = 'inline-block' + + const options = [ + '-', + 'Select Neighbors', + 'Select MultiLayer Path', + 'Select Downstream Path', + ] + if (node.url !== '') options.push('Link') + if (node.descr !== '') options.push('Description') + for (const text of options) { + const option = document.createElement('option') + option.text = text + select.appendChild(option) + } + + select.addEventListener('change', () => { + executeCommand(nodeIndex, select.options[select.selectedIndex].text) + }) + return select +} + +export function executeCommand(nodeIndex: number, option: string): void { + const node = ctx.nodeObjects[nodeIndex] + if (option === '-') return + + if (option === 'Select Neighbors') selectNeighbors(nodeIndex) + else if (option === 'Select MultiLayer Path') selectMultiLayerPath(nodeIndex) + else if (option === 'Select Downstream Path') selectDownstreamPath(nodeIndex) + else if (option === 'Link') window.open(node.url) + // v2 'Description' filled #descrDiv — Phase 13 UI panel + + finishCommand() +} From aeef6e7f4157a42d6b35095fd645e759b792d01e Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 10:39:37 +0100 Subject: [PATCH 028/166] frontend: layer DragControls port; delete v2 drag_controls.js + matrix4.js Hand-port of v2's tweaked three.js DragControls copy onto npm three math (the matrix4.js shim goes away). v2 behavior kept: drag engages only while the left button is held over a hovered layer plane, label render flags raised while dragging, camera-facing drag plane with parent-space offset. On release it now raises the inter-layer edge render flag and emits layer:moved (v2 routed this through Shiny syncs). The planes list is read live from ctx so a single registration survives network reloads (v2 recreated DragControls per load). Registered from main.ts. Both v2 living-spec files deleted; MIGRATION.md rows ticked. Co-Authored-By: Claude Fable 5 --- MIGRATION.md | 6 +- PLAN.md | 3 +- frontend/src/actions/drag_controls.test.ts | 76 ++ frontend/src/actions/drag_controls.ts | 145 ++++ frontend/src/main.ts | 2 + www/js/three/drag_controls.js | 210 ----- www/js/three/matrix4.js | 881 --------------------- 7 files changed, 228 insertions(+), 1095 deletions(-) create mode 100644 frontend/src/actions/drag_controls.test.ts create mode 100644 frontend/src/actions/drag_controls.ts delete mode 100644 www/js/three/drag_controls.js delete mode 100644 www/js/three/matrix4.js diff --git a/MIGRATION.md b/MIGRATION.md index ab5c5d3..86832de 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -37,8 +37,8 @@ See `PLAN.md` for the ordered implementation steps. | Old file | New file | Done | |---|---|---| | `www/js/three/three.js` *(NOT patched — see below)* | `three` npm package (r170) | [x] | -| `www/js/three/matrix4.js` | `three` npm package (bundled) — kept as Phase 11 spec | [ ] | -| `www/js/three/drag_controls.js` | `three/examples/jsm/controls/DragControls` (npm) — kept as Phase 11 spec | [ ] | +| `www/js/three/matrix4.js` | `three` npm package (bundled) | [x] | +| `www/js/three/drag_controls.js` | `src/actions/drag_controls.ts` (hand-ported; npm three math) | [x] | | `www/js/classes/Scene.js` | `frontend/src/three/Scene.ts` | [x] | | `www/js/classes/Layer.js` | `frontend/src/three/Layer.ts` | [x] | | `www/js/classes/Node.js` | `frontend/src/three/Node.ts` | [x] | @@ -53,7 +53,7 @@ See `PLAN.md` for the ordered implementation steps. > `src/three/runtime.ts` context; static geometry/palette constants by > `src/three/constants.ts`. All four classes now live under `src/three/` > (not the SPEC's split `classes/` + `three/`), since with no patches one folder -> suffices. `matrix4.js` / `drag_controls.js` stay until Phase 11 (canvas_controls). +> suffices. `matrix4.js` / `drag_controls.js` deleted with `src/actions/drag_controls.ts`. | `www/js/config/global_variables.js` | `frontend/src/config/global_variables.ts` + `GET /api/config` | [ ] | | `www/js/config/static_variables.js` | `frontend/src/config/static_variables.ts` | [ ] | | `www/js/object_actions/canvas_controls.js` | `frontend/src/actions/canvas_controls.ts` | [ ] | diff --git a/PLAN.md b/PLAN.md index 61cb4bf..c8bc496 100644 --- a/PLAN.md +++ b/PLAN.md @@ -153,7 +153,8 @@ Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2 - [x] `layer.js` → `src/actions/layer.ts` — `initialSpreadLayers` (moved from network.ts), hover (`checkHoverOverLayer` red-highlight + repaint), double-click/`selectLayer`/select-all selection (store-synced), `repaintLayers` (selected → default/picker priority), per-layer visibility + node-label toggles (recompute node label flags), coords/opacity/wireframe/color-priority setters. New ctx state: `lastHoveredLayerIndex`, `hoveredLayerPaintedFlag`, `renderLayerLabelsFlag`. 6 Vitest tests. *(Checkbox group + picker DOM → Phase 13; DragControls + held-key/slider rotate/move/scale intervals → canvas_controls.ts.)* - [x] `themes.js` → `src/actions/themes.ts` — `THEMES` presets (light/dark/gray from v2 buttons), `applyTheme(name, fromInit)` (renderer clear color, `ctx.edgeDefaultColor`/`labelColor`, channel palette from config, picker-priority layer repaint with theme floor color), `registerThemeListener()` wired to `theme:changed` in main.ts so `ChangeThemeCommand` execute/undo recolors. Added `ctx.labelColor` (v2 `globalLabelColor`), `repaintLayers(pickerColor?)`, `assignChannelColorsFromPalette` in edge.ts. 4 Vitest tests. *(Theme buttons DOM + channel edit list rebuild → Phase 13; label recolor → labels.ts.)* - [x] `labels.js` → `src/actions/labels.ts` — v2's plain absolutely-positioned divs in `#labelDiv` (kept over CSS2DRenderer: no new dep, inline `position:absolute` until the Phase 13 stylesheet), `createLabels` on network build, flag-gated `renderLayer/NodeLabels` registered as animate hooks (`registerAnimateHook` added to screen.ts — avoids screen→labels→node→screen cycle), `showLayer/NodeLabels` modes, resize + `setLabelColor` (themes now recolors labels). Added `ctx.renderNodeLabelsFlag` + layer-label flags; `decideNodeLabelFlags` raises the render flag as v2 did. 4 Vitest tests (flag machinery; div positioning → Phase 13 Playwright). `index.html` gains `#labelDiv`. -- [x] `canvas_controls.js` (core) + canvas half of `event_listeners.js` → `src/actions/canvas_controls.ts` — `sceneZoom`, `keyPressed`/`axisRelease` (modern `event.key`), `clickDown/Drag/Up`, `dblClick` (node → layer → unselect-all priority), scene pan/orbit, held-key node-translate/layer-rotate (the node.js/layer.js deferrals), shift-lasso select (anchor, THREE.Line rectangle, dim-then-select), `registerCanvasControls()` on the canvas from main.ts (tabIndex, wheel/scroll prevention, mouseleave release). 8 Vitest tests. *(Right-click node menu → right_click_menu.ts; DragControls layer dragging still open; nav buttons/slider control table DOM → Phase 13.)* +- [x] `canvas_controls.js` (core) + canvas half of `event_listeners.js` → `src/actions/canvas_controls.ts` — `sceneZoom`, `keyPressed`/`axisRelease` (modern `event.key`), `clickDown/Drag/Up`, `dblClick` (node → layer → unselect-all priority), scene pan/orbit, held-key node-translate/layer-rotate (the node.js/layer.js deferrals), shift-lasso select (anchor, THREE.Line rectangle, dim-then-select), `registerCanvasControls()` on the canvas from main.ts (tabIndex, wheel/scroll prevention, mouseleave release). 8 Vitest tests. *(Right-click node menu → right_click_menu.ts; nav buttons/slider control table DOM → Phase 13.)* +- [x] DragControls layer dragging → `src/actions/drag_controls.ts` — hand-port of v2's tweaked `www/js/three/drag_controls.js` on npm three math (`matrix4.js` shim dropped; both v2 files deleted). v2 gate kept (drag only while left button held over hovered layer; label flags raised); release now raises the inter-edge render flag + emits `layer:moved`. Planes list read live from ctx, so one registration survives network reloads. 4 Vitest tests (ray-hit select, ortho drag math, gate, release). - [x] `right_click_menu.js` + `replaceContextMenuOverNode` (event_listeners.js) → `src/actions/right_click_menu.ts` — pure graph commands (`selectNeighbors`, `selectMultiLayerPath` with v2's starting/current-layer exclusions, recursive `selectDownstreamPath` over inter-layer edges), `executeCommand` tail (render flags + label flags + store sync), `` menu over the clicked node in `#labelDiv` (Link opens URL; Description → Phase 13 panel), wired to `contextmenu` + removed on `clickUp` in canvas_controls. 5 Vitest tests. *(Loader spinner + descrDiv content → Phase 13.)* -- [ ] Delete `www/js/object_actions/` +- [ ] Delete `www/js/object_actions/` — **partially done**: `screen.js`, `labels.js`, `themes.js`, `right_click_menu.js` deleted (fully ported). Kept as living spec for their unported remainders: `layout.js` (predefined/star/cube layouts), `network.js` (`importNetwork` session JSON), `node.js`/`edge.js`/`layer.js`/`canvas_controls.js` (Phase 13 DOM: sliders, checkbox group, channel lists, descr div, nav table). --- @@ -170,7 +170,7 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Migrate `on_page_load.js` → `src/main.ts` — config fetch → renderer/camera/scene setup → mount canvas → animate; exposes `window.__arena = { ctx, history }` test hook (Phase 13 Playwright reads scene state through it) - [x] **Canvas mouse/keyboard scene controls** (`clickDown/Drag/Up`, `sceneZoom`, `keyPressed`, right-click menu) — landed in `canvas_controls.ts` + `right_click_menu.ts` (see Phase 11 list). - [ ] Replace `rshiny_handlers.js` + `rshiny_update.js` fully with EventBus + API (foundation done: bus/store/api client + commands already dispatch events; remaining handlers map to the Phase 11-deferred actions + Phase 13 panels) -- [ ] Delete `www/js/event_listeners.js`, `www/js/on_page_load.js`; `www/js/general.js` → **can delete now** (fully ported to `src/utils.ts`) +- [x] Delete `www/js/event_listeners.js`, `www/js/on_page_load.js`, `www/js/general.js` — all fully ported (`event_listeners.ts` + `canvas_controls.ts` + `right_click_menu.ts`; `main.ts`; `utils.ts`). The v2 color-picker inputs from `on_page_load.js` re-appear with the Phase 13 scene panel (`repaintLayers` already reads `#floor_color`). - [ ] Delete `www/js/rshiny_handlers.js`, `www/js/rshiny_update.js` - [ ] Delete `www/js/config/` diff --git a/www/js/event_listeners.js b/www/js/event_listeners.js deleted file mode 100644 index 066cca1..0000000 --- a/www/js/event_listeners.js +++ /dev/null @@ -1,240 +0,0 @@ -// on mouse wheel scroll -const sceneZoom = async (event) => { - if (scene.exists()) { - scene.zoom(event.deltaY); - - await delay_ms(100); - updateScenePanRShiny(); - updateVRLayerLabelsRShiny(); - updateVRNodesRShiny(); - } -}; - -// on arrow keys press or axis select to rotate layers -const keyPressed = (event) => { - if (scene.exists()) { - let code = event.keyCode; - if (code == 90) - scene.axisPressed = 'z'; - else if (code == 88) - scene.axisPressed = 'x'; - else if (code == 67) - scene.axisPressed = 'c'; - else if (code == 37 || code == 38 || code == 39 || code == 40) { - scene.translatePanWithArrow(code); - - updateScenePanRShiny(); - updateVRLayerLabelsRShiny(); - updateVRNodesRShiny(); - } - } -}; - -const axisRelease = () => { - if (scene.exists()) - scene.axisPressed = ""; -}; - -// mouse keys press event -const clickDown = (event) => { - if (scene.exists()) { - //mouse: 0 left, 1 middle, 2 right click - if (event.button == 0) { - scene.leftClickPressed = true; - scene.middleClickPressed = false; - if (event.shiftKey && shiftX === "") { - shiftX = event.layerX - xBoundMax; - shiftY = yBoundMax - event.layerY; // used in drag - } - } else if (event.button == 1) { - scene.middleClickPressed = true; - scene.leftClickPressed = false; - } else { - scene.middleClickPressed = false; - scene.leftClickPressed = false; - } - } -}; - -// while mouse button held, drag event -const clickDrag = (event) => { - if (scene.exists()) { - let distance = Math.sqrt( - Math.pow(mousePreviousX - event.screenX, 2) + Math.pow(mousePreviousY - event.screenY, 2) - ); - - if (distance > 10) { - let x = event.screenX, y = event.screenY; - - if (scene.leftClickPressed) { - let selectedNodePositions = getSelectedNodes(); - - scene.dragging = true; - if (event.shiftKey) { - lastHoveredLayerIndex = ""; // to be able to lasso inside layer - lassoSelectNodes(event.layerX - xBoundMax, yBoundMax - event.layerY); - } else if (scene.axisPressed !== "" && selectedNodePositions.length > 0) - translateNodesWithHeldKey(event); - else if (scene.axisPressed !== "") - rotateLayersWithHeldKey(event); - else if (lastHoveredLayerIndex === "" && last_hovered_node_index === "") - sceneDragPan(x, y); - } else if (scene.middleClickPressed) { - scene.dragging = true; - event.preventDefault(); - sceneOrbit(x, y); - } - - mousePreviousX = x; - mousePreviousY = y; - } - - if (!scene.leftClickPressed && !scene.middleClickPressed) - if (!checkHoverOverNode(event)) - checkHoverOverLayer(event); - } -}; - -// left-click drag on scene -const sceneDragPan = async (x, y) => { - scene.translatePanWithMouse(x, y); - - await delay_ms(100); - updateScenePanRShiny(); - updateVRLayerLabelsRShiny(); - updateVRNodesRShiny(); -}; - -// middle-click drag on scene -const sceneOrbit = async (x, y) => { - scene.orbitSphereWithMouse(x, y); - - await delay_ms(100); - updateSceneSphereRShiny(); - updateVRLayerLabelsRShiny(); - updateVRNodesRShiny(); -}; - -const clickUp = (event) => { - if (scene.exists()) { - scene.dragging = false; - if (event.button == 0) { - scene.leftClickPressed = false; - if (optionsList !== "") { - document.getElementById("labelDiv").removeChild(optionsList); - optionsList = ""; - } - if (lasso !== "") { - for (let i = 0; i < nodeObjects.length; i++) { - if (nodeObjects[i].getOpacity() == 0.5) { // means is inside lasso event - nodeObjects[i].setOpacity(1); - nodeObjects[i].isSelected = true; - repaintNode(i); - } - } - decideNodeLabelFlags(); - updateSelectedNodesRShiny(); - } - - shiftX = ""; - shiftY = ""; - scene.remove(lasso); - lasso = ""; - } else if (event.button == 1) - scene.middleClickPressed = false; - } -}; - -// double click event (left mouse), select node -> select layer -> unselect all nodes -const dblClick = (event) => { - if (scene.exists()) { - // Priorities: - if (!performDoubleClickNodeSelection(event)) { // 1. select node - if (lastHoveredLayerIndex !== "") { // 2. select layer - performDoubleClickLayerSelection(); - } else { // 3. unselect all nodes - unselectAllNodes(); - unselectAllEdges(); - - redrawIntraLayerEdges(); - updateSelectedEdgesRShiny(); - } - } - } -}; - -// right mouse click on node -const replaceContextMenuOverNode = (event) => { - if (optionsList !== "") { - document.getElementById("labelDiv").removeChild(optionsList); - optionsList = ""; - } - - for (let i = 0; i < nodeObjects.length; i++) { - let nodeX = xBoundMax + nodeObjects[i].getWorldPosition("x"), - nodeY = yBoundMax - nodeObjects[i].getWorldPosition("y"); - - if (rightClickedOnNode(nodeX, nodeY, event)) { - let option; - - event.preventDefault(); - - // creating list and appending to 3d-graph div - optionsList = document.createElement("select"); - optionsList.setAttribute('class', 'optionsBox'); - optionsList.setAttribute('id', 'mySelect'); - optionsList.setAttribute('onchange', 'executeCommand(this)'); - optionsList.style.left = nodeX.toString().concat("px"); - optionsList.style.top = nodeY.toString().concat("px"); - optionsList.style.display = "inline-block"; - - // Do nothing - option = document.createElement("option"); - option.value = ""; - option.text = "-"; - optionsList.appendChild(option); - - // Neighbors - option = document.createElement("option"); - option.value = i; - option.text = "Select Neighbors"; - optionsList.appendChild(option); - - // MultiLayer Path - option = document.createElement("option"); - option.value = i; - option.text = "Select MultiLayer Path"; - optionsList.appendChild(option); - - // Downstream Path - option = document.createElement("option"); - option.value = i; - option.text = "Select Downstream Path"; - optionsList.appendChild(option); - - // Link - if (nodeObjects[i].url != "") { - option = document.createElement("option"); - option.value = nodeObjects[i].url; - option.text = "Link"; - optionsList.appendChild(option); - } - - // Description - if (nodeObjects[i].descr != "") { - option = document.createElement("option"); - option.value = nodeObjects[i].descr; - option.text = "Description"; - optionsList.appendChild(option); - } - - document.getElementById("labelDiv").appendChild(optionsList); - - break; - } - } -}; - -const rightClickedOnNode = (nodeX, nodeY, event) => { - return(Math.pow(nodeX - event.layerX, 2) + Math.pow(nodeY - event.layerY, 2) <= Math.pow((SPHERE_RADIUS + 1), 2)) -}; diff --git a/www/js/object_actions/labels.js b/www/js/object_actions/labels.js deleted file mode 100644 index c46e808..0000000 --- a/www/js/object_actions/labels.js +++ /dev/null @@ -1,196 +0,0 @@ -// Initialization ====== -const createLabels = () => { - createNodeLabels(); - createLayerLabels(); -}; - -const createNodeLabels = () => { - let div, nodeNames = nodeObjects.map(({ name }) => name); - - for (let i = 0; i < nodeNames.length; i++) { - div = document.createElement('div'); - div.textContent = nodeNames[i]; - div.setAttribute('class', 'labels'); - div.setAttribute('id', nodeLayerNames[i].concat("_label")); - document.getElementById("labelDiv").appendChild(div); - - nodeLabelsDivs.push(div); - nodeLabelsDivs[i].style.fontSize = NODE_LABEL_DEFAULT_SIZE; - nodeLabelsDivs[i].style.display = "none"; - nodeLabelsDivs[i].style.color = globalLabelColor; - } -}; - -const createLayerLabels = () => { - let div, name; - - for (let i = 0; i < layers.length; i++) { - name = layers[i].getName(); - div = document.createElement('div'), - div.textContent = name; - div.setAttribute('class', 'layer-labels'); - div.setAttribute('id', name.concat("_label")); - document.getElementById("labelDiv").appendChild(div); - - layerLabelsDivs.push(div); - layerLabelsDivs[i].style.display = "inline-block"; - layerLabelsDivs[i].style.color = globalLabelColor; - } -}; - -const setLabelColorVariable = (labelColor) => { - globalLabelColor = labelColor; - setLabelColor(); -}; - -const setLabelColor = () => { - let i; - - for (i = 0; i < layerLabelsDivs.length; i++) - layerLabelsDivs[i].style.color = globalLabelColor; - - for (i = 0; i < nodeLabelsDivs.length; i++) - nodeLabelsDivs[i].style.color = globalLabelColor; -}; - -// Handlers ====== -const showLayerLabels = (mode) => { - if (mode == "all") { - showAllLayerLabels(); - } else if (mode == "selected") { - showSelectedLayerLabels(); - } else if (mode == "none") { - hideAllLayerLabels(); - } -}; - -const showAllLayerLabels = () => { - setGlobalLayerLabelFlags(true, false); - - for (let i = 0; i < layers.length; i++) - layerLabelsDivs[i].style.display = "inline-block"; - - renderLayerLabelsFlag = true; -}; - -const setGlobalLayerLabelFlags = (allFlag, selectedFLag) => { - showAllLayerLabelsFlag = allFlag; - showSelectedLayerLabelsFlag = selectedFLag; -}; - -const showSelectedLayerLabels = () => { - setGlobalLayerLabelFlags(false, true); - - for (let i = 0; i < layers.length; i++) { - if (layers[i].isSelected) - layerLabelsDivs[i].style.display = "inline-block"; - else - layerLabelsDivs[i].style.display = "none"; - } - - renderLayerLabelsFlag = true; -}; - -const hideAllLayerLabels = () => { - setGlobalLayerLabelFlags(false, false); - - for (let i = 0; i < layers.length; i++) - layerLabelsDivs[i].style.display = "none"; -}; - -const resizeLayerLabels = (size) => { - for (let i = 0; i < layerLabelsDivs.length; i++) - layerLabelsDivs[i].style.fontSize = size.toString().concat("px"); -}; - -const showNodeLabels = (mode) => { - if (mode == "all") { - showAllNodeLabelsFlag = true; - showSelectedNodeLabelsFlag = false; - } else if (mode == "selected") { - showAllNodeLabelsFlag = false; - showSelectedNodeLabelsFlag = true; - } else if (mode == "none") { - showAllNodeLabelsFlag = false; - showSelectedNodeLabelsFlag = false; - } - decideNodeLabelFlags(); -}; - -const resizeNodeLabels = (size) => { - for (let i = 0; i < nodeLabelsDivs.length; i++) - nodeLabelsDivs[i].style.fontSize = size.toString().concat("px"); -}; - -// on animate ====== -const renderLayerLabels = () => { - if (renderLayerLabelsFlag) { - - if (showAllLayerLabelsFlag) - redrawLayerLabels("all"); - else { - if (showSelectedLayerLabelsFlag) { - let selected_layers = getSelectedLayers(); - - removeAllLayerLabels(); - if (selected_layers.length > 0) - redrawLayerLabels("selected"); - } - } - - renderLayerLabelsFlag = false; - } -}; - -const removeAllLayerLabels = () => { - for (let i = 0; i < layers.length; i++) - layerLabelsDivs[i].style.display = "none"; -}; - -const redrawLayerLabels = (mode) => { - let layerArray, position, - hidelayerCheckboxes = document.getElementsByClassName("hideLayer_checkbox"), - layerX, layerY, labelX, labelY, - layer_spheres = layers.map(({ sphere }) => sphere); - - layerArray = mode === "all" ? layers.map(({ name }) => name) : getSelectedLayers(); - - for (let i = 0; i < layerArray.length; i++) { - position = mode == "selected" ? layerArray[i] : i; - - if (!hidelayerCheckboxes[position].checked) { - layerX = layer_spheres[position].getWorldPosition(new THREE.Vector3()).x, - layerY = layer_spheres[position].getWorldPosition(new THREE.Vector3()).y; - labelX = xBoundMax + layerX; - labelY = yBoundMax - layerY; - - layerLabelsDivs[position].style.left = labelX.toString().concat("px"); - layerLabelsDivs[position].style.top = labelY.toString().concat("px"); - layerLabelsDivs[position].style.display = "inline-block"; - } else - layerLabelsDivs[position].style.display = "none"; - } -}; - -const renderNodeLabels = () => { - if (renderNodeLabelsFlag) { - - let nodeX, nodeY, labelX, labelY; - - for (let i = 0; i < nodeObjects.length; i++) { - if (nodeObjects[i].showLabel) { - nodeX = nodeObjects[i].getWorldPosition("x"); - nodeY = nodeObjects[i].getWorldPosition("y"); - labelX = xBoundMax + nodeX + 7; - labelY = yBoundMax - nodeY - 10; - - nodeLabelsDivs[i].style.left = labelX.toString().concat("px"); - nodeLabelsDivs[i].style.top = labelY.toString().concat("px"); - nodeLabelsDivs[i].style.display = "inline-block"; - } else - nodeLabelsDivs[i].style.display = "none"; - } - - renderNodeLabelsFlag = false; - } -}; diff --git a/www/js/object_actions/right_click_menu.js b/www/js/object_actions/right_click_menu.js deleted file mode 100644 index f8b87cc..0000000 --- a/www/js/object_actions/right_click_menu.js +++ /dev/null @@ -1,164 +0,0 @@ -const executeCommand = (item) => { - let selectedOption = item.options[item.selectedIndex].text; - - if (selectedOption !== "-") { - startLoader(); - if (selectedOption == "Select Neighbors") { - executeSelectNeightborsCommand(item); - } else if (selectedOption == "Select MultiLayer Path") { - executeSelectMultiLayerPathCommand(item); - } else if (selectedOption == "Select Downstream Path") { - executeSelectDownstreamPathCommand(item); - } else if (selectedOption == "Link") { - window.open(item.value); - } else if (selectedOption == "Description") { - let descrDiv = document.getElementById("descrDiv"), - p = descrDiv.getElementsByTagName('p')[0]; - - p.innerHTML = item.value; - descrDiv.style.display = "inline-block"; - } - - renderInterLayerEdgesFlag = true; - redrawIntraLayerEdges(); - decideNodeLabelFlags(); - updateSelectedNodesRShiny(); - updateSelectedEdgesRShiny(); - - finishLoader(); - } -}; - -const executeSelectNeightborsCommand = (item) => { - let index1, index2; - - for (let i = 0; i < edgeObjects.length; i++) { - index1 = nodeLayerNames.indexOf(edgeObjects[i].source); - index2 = nodeLayerNames.indexOf(edgeObjects[i].target); - - if (index1 == item.value) - selectNodeAndEdge(index2, i); - else if (index2 == item.value) - selectNodeAndEdge(index1, i); - } -}; - -const selectNodeAndEdge = (nodeIndex, edgeIndex) => { - nodeObjects[nodeIndex].isSelected = true; - repaintNode(nodeIndex); - - edgeObjects[edgeIndex].select(); -}; - -const executeSelectMultiLayerPathCommand = (item) => { - let flag = false, index1, index2, - tempSelectedNodes = [], - currentNode = item.value, - startingLayer = nodeGroups[nodeLayerNames[currentNode]], - checkedNodes = [], - arrayDifference; - - while (!flag) { - for (let i = 0; i < edgeObjects.length; i++) { - index1 = nodeLayerNames.indexOf(edgeObjects[i].source); - index2 = nodeLayerNames.indexOf(edgeObjects[i].target); - - if (index1 == currentNode && - nodeGroups[nodeLayerNames[index2]] != startingLayer && // path must not contain other nodes in starting layer - nodeGroups[nodeLayerNames[index2]] != nodeGroups[nodeLayerNames[currentNode]] && // or its own layer - !(exists(tempSelectedNodes, index2))) { - tempSelectedNodes.push(index2); - selectNodeAndEdge(index2, i); - } else if (index2 == currentNode && - nodeGroups[nodeLayerNames[index1]] != startingLayer && - nodeGroups[nodeLayerNames[currentNode]] != nodeGroups[nodeLayerNames[index1]] && - !(exists(tempSelectedNodes, index1))) { - tempSelectedNodes.push(index1); - selectNodeAndEdge(index1, i); - } - } - - // decide flag for exit, if no unchecked nodes - checkedNodes.push(currentNode); - arrayDifference = tempSelectedNodes.filter(x => !checkedNodes.includes(x)); - if (arrayDifference.length === 0) - flag = true; - else - currentNode = arrayDifference[0]; - } -}; - -const executeSelectDownstreamPathCommand = (item) => { - let currentNode = item.value, - layerPath = [nodeGroups[nodeLayerNames[currentNode]]]; // begin with array of 1 layer - - recursiveDownstreamHighlight(layerPath, currentNode); - downstreamCheckedNodes = []; // resetting global variable -}; - -// This function highlights recursively downstream nodes from the clicked node on the starting layer. -// Makes use of global variable downstreamCheckedNodes (int array) to keep track of already checked nodes for avoiding re-checks -// @param layerPath (String Array): current path's already checked Layers -// @param currentNode (integer): current node, whose Layer must be the last in the layerPath array -// @param previousNode (integer): previous node, to figure which edge to paint -const recursiveDownstreamHighlight = (layerPath, currentNode, previousNode = null) => { - let interLayerEdge, neighbors, toCheckLayer; - - if (!exists(downstreamCheckedNodes, currentNode)) { - downstreamCheckedNodes.push(currentNode); - - if (previousNode !== null) { // skipping first node call - interLayerEdge = getInterLayerEdge(currentNode, previousNode); - if (interLayerEdge !== null) - selectNodeAndEdge(currentNode, interLayerEdge); - } - - // find node inter-layer neighbors and continue recursively - neighbors = getInterLayerNeighbors(currentNode); - for (let i = 0; i < neighbors.length; i++) { - toCheckLayer = nodeGroups[nodeLayerNames[neighbors[i]]]; - if (!exists(layerPath, toCheckLayer)) { - layerPath.push(toCheckLayer); - recursiveDownstreamHighlight(layerPath, neighbors[i], currentNode); - layerPath.pop(); - } - } - } -}; - -// This function returns the inter-layer edge connecting two nodes if exists, else null -const getInterLayerEdge = (node1, node2) => { - let index1, index2; - - for (let i = 0; i < edgeObjects.length; i++) { - if (edgeObjects[i].interLayer) { - index1 = nodeLayerNames.indexOf(edgeObjects[i].source); - index2 = nodeLayerNames.indexOf(edgeObjects[i].target); - - if ((node1 == index1 && node2 == index2) || - (node1 == index2 && node2 == index1)) - return(i) - } - } - - return(null) -}; - -// This function returns the node IDs of requested node's inter-layer neighbors -// @param node (integer): node whose neighbors are requested -// @return int array of neighbor IDs -const getInterLayerNeighbors = (node) => { - let index1, index2, neighbors = []; - - for (let i = 0; i < edgeObjects.length; i++) { - index1 = nodeLayerNames.indexOf(edgeObjects[i].source); - index2 = nodeLayerNames.indexOf(edgeObjects[i].target); - - if (node == index1) - neighbors.push(index2); - else if (node == index2) - neighbors.push(index1); - } - - return(neighbors) -}; diff --git a/www/js/object_actions/screen.js b/www/js/object_actions/screen.js deleted file mode 100644 index c5f5368..0000000 --- a/www/js/object_actions/screen.js +++ /dev/null @@ -1,58 +0,0 @@ -const setRenderer = () => { - renderer = new THREE.WebGLRenderer({ antialias: true }); -}; - -const resetScreen = () => { - setWindowBounds(); - setCamera(); - resizeRenderer(); -} - -const setWindowBounds = () => { - xBoundMin = -window.innerWidth/2, - xBoundMax = window.innerWidth/2, - yBoundMin = -window.innerHeight/2, - yBoundMax = window.innerHeight/2, - zBoundMin = -window.innerHeight/2.5, - zBoundMax = window.innerHeight/2.5; -} - -const setCamera = () => { - // camera: left, right, top, bottom, near, far - camera = new THREE.OrthographicCamera(xBoundMin, xBoundMax, - yBoundMax, yBoundMin, -4 * xBoundMax, 4 * xBoundMax); - camera.position.set(0, 0, 100); - camera.lookAt(0, 0, 0); -} - -const resizeRenderer = () => { - renderer.setSize(2 * xBoundMax , 2 * yBoundMax); -} - -const setRendererColor = (hexColor) => { - if (scene.exists()) { - renderer.setClearColor(hexColor); - updateScenePanRShiny(); - } -} - -const setRaycaster = (event) => { - RAYVECTOR.set((event.clientX / window.innerWidth) * 2 - 1, - -(event.clientY / window.innerHeight) * 2 + 1, -1 ); // z = - 1 important! - RAYVECTOR.unproject(camera); - RAYDIR.set(0, 0, -1).transformDirection(camera.matrixWorld); - RAYCASTER.set(RAYVECTOR, RAYDIR); -} - -// 3D graphics render animation ==================== -const animate = () => { - setTimeout(function() { // limiting FPS - requestAnimationFrame(animate); // pauses when the user navigates to another browser tab - }, 1000 / fps); - - renderInterLayerEdges(); - renderLayerLabels(); - renderNodeLabels(); - - renderer.render(scene.THREE_Object, camera); -} diff --git a/www/js/object_actions/themes.js b/www/js/object_actions/themes.js deleted file mode 100644 index efd8563..0000000 --- a/www/js/object_actions/themes.js +++ /dev/null @@ -1,61 +0,0 @@ -const attachThemeButtons = () => { - let themeDiv = document.getElementById("themeDiv"), - lightThemeButton = document.createElement("button"), - darkThemeButton = document.createElement("button"), - grayThemeButton = document.createElement("button"); - - lightThemeButton.className = "themeButton"; - lightThemeButton.id = "lightThemeButton"; - lightThemeButton.innerHTML = "Light"; - - darkThemeButton.className = "themeButton"; - darkThemeButton.id = "darkThemeButton"; - darkThemeButton.innerHTML = "Dark"; - - grayThemeButton.className = "themeButton"; - grayThemeButton.id = "grayThemeButton"; - grayThemeButton.innerHTML = "Gray"; - - lightThemeButton.addEventListener("click", () => { - applyTheme('#ffffff', '#8aa185', '#5c5c5c', - CHANNEL_COLORS_DARK, '#000000') - }); - darkThemeButton.addEventListener("click", () => { - applyTheme('#000000', '#777777', '#ffffff', - CHANNEL_COLORS_LIGHT, '#ffffff') - }); - grayThemeButton.addEventListener("click", () => { - applyTheme('#999999', '#1d4991', '#6e2a5a', - CHANNEL_COLORS_LIGHT, '#ffffff') - }); - - themeDiv.appendChild(lightThemeButton); - themeDiv.appendChild(darkThemeButton); - themeDiv.appendChild(grayThemeButton); -}; - -const applyTheme = (bgColor, floorColor, - edgeColor, channel_colors_theme, labelColor, - fromInit = false) => { - if (scene.exists()) { - setRendererColor(bgColor); - document.getElementById("floor_color").value = floorColor; - EDGE_DEFAULT_COLOR = edgeColor; // global for inter-layer edges - assignChannelColorsFromPalette(channel_colors_theme); - globalLabelColor = labelColor; - - if (!fromInit) { - repaintLayersFromPicker(); - attachChannelEditList(); // for new channel colors - redrawIntraLayerEdges(); - setLabelColor(); - - updateScenePanRShiny(); - updateLayersRShiny(); - updateNodesRShiny(); - updateVRNodesRShiny(); - updateEdgeColorsRShiny(); - updateLabelColorRShiny(); - } - } -}; diff --git a/www/js/on_page_load.js b/www/js/on_page_load.js deleted file mode 100644 index 3a27144..0000000 --- a/www/js/on_page_load.js +++ /dev/null @@ -1,65 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - setRenderer(); - resetScreen(); - scene = new Scene(); - - initializeCanvasDiv(); - addCanvasEventListeners(); - attachThemeButtons(); - initializeColorPickers(); - createNodeDescriptionDiv(); - - // other events - window.onresize = resetScreen; - // release mouse buttons on network div exit - document.getElementsByClassName("container-fluid")[0].children[0].addEventListener("mouseleave", clickUp); - let nodeSearchBar = document.getElementById("nodeSearchBar"); - nodeSearchBar.onkeydown = selectSearchedNodes; -}, false); - -const initializeCanvasDiv = () => { - let canvas_div = document.getElementById("3d-graph"); - canvas_div.style.position='fixed'; // to scroll down along with the page - //remove default scroll controls while hovering on canvas div - canvas_div.addEventListener("keydown", function(e) { - // space and arrow keys - if([32, 37, 38, 39, 40].indexOf(e.key) > -1) { - e.preventDefault(); - } - }, false); - // removing scroll controls from mouse navigation - canvas_div.addEventListener("mousedown", function(e) { - // middle mouse click - if(e.button == 1) { - e.preventDefault(); - } - }, false); - canvas_div.addEventListener("mousewheel", function(e) { - e.preventDefault(); - }, false); - canvas_div.appendChild(renderer.domElement); //create canvas element once -}; - -const addCanvasEventListeners = () => { - let canvas = document.getElementsByTagName("canvas")[0]; - canvas.tabIndex = 1; // default value = -1, giving focus on canvas so it can register keydown events - canvas.addEventListener("wheel", sceneZoom); - canvas.addEventListener("keydown", keyPressed); - canvas.addEventListener("keyup", axisRelease); - canvas.addEventListener('mousedown', clickDown); - canvas.addEventListener('mousemove', clickDrag); - canvas.addEventListener('mouseup', clickUp); - canvas.addEventListener('dblclick', dblClick); - canvas.addEventListener('contextmenu', replaceContextMenuOverNode); // implementing right click toolbox over nodes -}; - -const initializeColorPickers = () => { - let sceneColorDiv = document.getElementById("sceneColorPicker"); - sceneColorDiv.innerHTML = ' '); - let floorColorDiv = document.getElementById("floorColorPicker"); - floorColorDiv.innerHTML = ' '); -}; From 5759e3d216819a82d684cbe1b36d9fb16e09e4c8 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 10:52:34 +0100 Subject: [PATCH 030/166] frontend: session import build (v2 importNetwork); delete network.js buildFromSession consumes the backend-normalized SessionData (now typed in api/client.ts): scene position/scale/rotation + renderer clear color, full layer transforms (initial spread only when the backend generate_coordinates flag says the JSON carried none), nodes with positions/scale/color/url/descr (scramble per flag within min layer width), edges with per-row file colors and opacity as the weight, and the v2 setJSONExtras flags (label color, direction, width-by-weight, edgeFileColorPriority). loadSession wraps it in LoadNetworkCommand as one undo step. createEdgeObjects gains the v2 decideEdgeColors rule: a row's own color wins over the channel palette. The store receives a NetworkData equivalent so later layout/topology calls work on imported sessions too. network.js is now fully ported and deleted; MIGRATION.md ticked. Session export + File-panel wiring land with Phase 13. Co-Authored-By: Claude Fable 5 --- MIGRATION.md | 2 +- PLAN.md | 5 +- frontend/src/actions/network.ts | 162 ++++++++++++-- frontend/src/actions/session.test.ts | 126 +++++++++++ frontend/src/api/client.ts | 59 ++++- www/js/object_actions/network.js | 322 --------------------------- 6 files changed, 336 insertions(+), 340 deletions(-) create mode 100644 frontend/src/actions/session.test.ts delete mode 100644 www/js/object_actions/network.js diff --git a/MIGRATION.md b/MIGRATION.md index daec225..cca6f4e 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -57,7 +57,7 @@ See `PLAN.md` for the ordered implementation steps. | `www/js/config/global_variables.js` | `frontend/src/config/global_variables.ts` + `GET /api/config` | [ ] | | `www/js/config/static_variables.js` | `frontend/src/config/static_variables.ts` | [ ] | | `www/js/object_actions/canvas_controls.js` | `frontend/src/actions/canvas_controls.ts` | [ ] | -| `www/js/object_actions/network.js` | `frontend/src/actions/network.ts` | [ ] | +| `www/js/object_actions/network.js` | `frontend/src/actions/network.ts` (upload build + session import) | [x] | | `www/js/object_actions/layout.js` | `frontend/src/actions/layout.ts` | [ ] | | `www/js/object_actions/layer.js` | `frontend/src/actions/layer.ts` | [ ] | | `www/js/object_actions/node.js` | `frontend/src/actions/node.ts` | [ ] | diff --git a/PLAN.md b/PLAN.md index d8f8a2b..084107f 100644 --- a/PLAN.md +++ b/PLAN.md @@ -147,7 +147,8 @@ Done now (portable core): Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2D) and Phase 13 (UI/DOM), then delete `www/js/object_actions/`: - [ ] `screen.js` → `src/actions/screen.ts` (renderer, camera, window bounds, raycaster, `animate()`) -- [x] `network.js` → `src/actions/network.ts` — `buildNetwork` consumes backend `NetworkData` (layer/edge limits enforced server-side; channel limit checked client-side); `loadNetwork` wraps it in `LoadNetworkCommand`. Builds a fresh `Scene` + registries (never mutates in place) so undo snapshots stay valid. Spread/scramble now imported from `layer.ts`/`node.ts`. 4 Vitest tests. *(DragControls/labels/channel-UI/importNetwork stay with their own actions.)* +- [x] `network.js` → `src/actions/network.ts` — `buildNetwork` consumes backend `NetworkData` (layer/edge limits enforced server-side; channel limit checked client-side); `loadNetwork` wraps it in `LoadNetworkCommand`. Builds a fresh `Scene` + registries (never mutates in place) so undo snapshots stay valid. Spread/scramble now imported from `layer.ts`/`node.ts`. 4 Vitest tests. *(Channel UI → Phase 13.)* +- [x] Session import → `network.ts` `buildFromSession`/`loadSession` (one undo step) — consumes the backend-normalized `SessionData` (typed in `api/client.ts`): scene position/scale/rotation + clear color, full layer transforms (spread only when the backend `generate_coordinates` flag says the JSON had none), nodes with positions/scale/color/url/descr (scramble per flag, min-layer-width bounds), edges with file colors (`EdgeRow.color` wins over channel palette) + opacity as weight, v2 `setJSONExtras` flags, store gets a `NetworkData` equivalent for later layout/topology calls. 6 Vitest tests. Session **export** + File-panel wiring → Phase 13. - [x] `node.js` → `src/actions/node.ts` — hover (`checkHoverOverNode`), double-click + search + select-all selection (store-synced), `repaintNode` (cluster branch folded into `Node.getColor`), label-flag priorities (DOM checkboxes → `Layer.isVisible`), shape/color-priority setters; `scrambleNodes` moved here from `network.ts`. New ctx state: `lastHoveredNodeIndex`, `selectedNodeColorFlag`, `showAll/showSelectedNodeLabelsFlag`. 7 Vitest tests. *(Search bar/sliders/descr div → Phase 13; lasso + held-key/spread/move/scale → canvas_controls.ts; attribute upload → POST /api/attributes.)* - [x] `edge.js` → `src/actions/edge.ts` — inter-layer render machinery (`renderInterLayerEdges` wired into `animate()`, remove-on-drag/pause/zero-opacity, v2 locked-flag redraw pair), `redrawIntra/InterLayerEdges` (moved out of `commands/scene.ts` privates), channel color/visibility, `unselectAllEdges`, all edge-setting setters. New ctx state: `renderInterLayerEdgesFlag`, `waitEdgeRenderFlag`, `interEdgesRemoved`, `interLayerEdgesRenderPauseFlag`. 5 Vitest tests. *(Edge build lives in network.ts; channel UI lists/picker → Phase 13; attributes upload → POST /api/attributes.)* - [x] `layer.js` → `src/actions/layer.ts` — `initialSpreadLayers` (moved from network.ts), hover (`checkHoverOverLayer` red-highlight + repaint), double-click/`selectLayer`/select-all selection (store-synced), `repaintLayers` (selected → default/picker priority), per-layer visibility + node-label toggles (recompute node label flags), coords/opacity/wireframe/color-priority setters. New ctx state: `lastHoveredLayerIndex`, `hoveredLayerPaintedFlag`, `renderLayerLabelsFlag`. 6 Vitest tests. *(Checkbox group + picker DOM → Phase 13; DragControls + held-key/slider rotate/move/scale intervals → canvas_controls.ts.)* @@ -156,7 +157,7 @@ Deferred into Phase 12 (with renderer/camera/animate/raycaster/DragControls/CSS2 - [x] `canvas_controls.js` (core) + canvas half of `event_listeners.js` → `src/actions/canvas_controls.ts` — `sceneZoom`, `keyPressed`/`axisRelease` (modern `event.key`), `clickDown/Drag/Up`, `dblClick` (node → layer → unselect-all priority), scene pan/orbit, held-key node-translate/layer-rotate (the node.js/layer.js deferrals), shift-lasso select (anchor, THREE.Line rectangle, dim-then-select), `registerCanvasControls()` on the canvas from main.ts (tabIndex, wheel/scroll prevention, mouseleave release). 8 Vitest tests. *(Right-click node menu → right_click_menu.ts; nav buttons/slider control table DOM → Phase 13.)* - [x] DragControls layer dragging → `src/actions/drag_controls.ts` — hand-port of v2's tweaked `www/js/three/drag_controls.js` on npm three math (`matrix4.js` shim dropped; both v2 files deleted). v2 gate kept (drag only while left button held over hovered layer; label flags raised); release now raises the inter-edge render flag + emits `layer:moved`. Planes list read live from ctx, so one registration survives network reloads. 4 Vitest tests (ray-hit select, ortho drag math, gate, release). - [x] `right_click_menu.js` + `replaceContextMenuOverNode` (event_listeners.js) → `src/actions/right_click_menu.ts` — pure graph commands (`selectNeighbors`, `selectMultiLayerPath` with v2's starting/current-layer exclusions, recursive `selectDownstreamPath` over inter-layer edges), `executeCommand` tail (render flags + label flags + store sync), ` + +
+ + +
+
+ + +
+
+ + +
+ + +
+ +` + +function status(msg: string, isError = false): void { + const el = document.getElementById('file_status') + if (!el) return + el.textContent = msg + el.className = `mt-3 small ${isError ? 'text-danger' : 'text-success'}` +} + +async function onUploadNetwork(file: File): Promise { + try { + const data = await api.uploadNetwork(file) + loadNetwork(data) + status(`Loaded network: ${data.layers.length} layers, ${data.nodes.length} nodes.`) + } catch (err) { + status(err instanceof Error ? err.message : 'Network upload failed.', true) + } +} + +async function onLoadSession(file: File): Promise { + try { + const session = await api.importSession(file) + loadSession(session) + status('Session loaded.') + } catch (err) { + status(err instanceof Error ? err.message : 'Session load failed.', true) + } +} + +async function onLoadExample(): Promise { + try { + const res = await fetch('/example_network.tsv') + const file = new File([await res.blob()], 'example_network.tsv', { + type: 'text/tab-separated-values', + }) + await onUploadNetwork(file) + } catch (err) { + status(err instanceof Error ? err.message : 'Example load failed.', true) + } +} + +export function initFilePanel(): void { + const pane = document.getElementById('panel-file') + if (!pane) return + pane.innerHTML = FILE_HTML + + const netInput = document.getElementById('input_network_file') as HTMLInputElement + netInput.addEventListener('change', () => { + if (netInput.files?.[0]) void onUploadNetwork(netInput.files[0]) + }) + + const sessInput = document.getElementById('load_network_file') as HTMLInputElement + sessInput.addEventListener('change', () => { + if (sessInput.files?.[0]) void onLoadSession(sessInput.files[0]) + }) + + document.getElementById('save_network_object')?.addEventListener('click', () => { + void exportSession().catch((err: unknown) => + status(err instanceof Error ? err.message : 'Save failed.', true) + ) + }) + + document.getElementById('exampleButton')?.addEventListener('click', () => { + void onLoadExample() + }) +} From 55cf514734f59914c8a0f0dac567f0fe6103c912 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 15:29:04 +0100 Subject: [PATCH 036/166] frontend: Layer Selection & Layouts panel port (views/layouts.R) Subgraph-scope radios, per-layer select/Hide/Labels checkbox group (rebuilt on network:loaded), channel layout list, layout/clustering/ local-layout selects wired to /api/layout, topology metric scaling wired to /api/topology. Verified live via Playwright against the example network. Co-Authored-By: Claude Fable 5 --- PLAN.md | 2 +- frontend/src/main.ts | 2 + frontend/src/ui/layouts.ts | 247 +++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 frontend/src/ui/layouts.ts diff --git a/PLAN.md b/PLAN.md index f5ea6b5..238c56c 100644 --- a/PLAN.md +++ b/PLAN.md @@ -188,7 +188,7 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Build `frontend/index.html` — Bootstrap 5 navbar (`data-bs-theme="dark"`, bootstrap css+js imported in `main.ts`) with the 11 v2 tabs as `data-bs-toggle="tab"` buttons over empty `#panel-*` panes (each `views/*.R` port fills its pane; Main View pane intentionally empty — selecting it reveals the canvas). v2 helper divs added (`#navControlButtonsDiv`, `#info`, `#loader`, `#descrDiv`). Verified live with playwright-cli (tabs listed in a11y tree, click switches active pane). - [x] Migrate Home panel (`views/home.R` → `src/ui/home.ts`) — static HTML into `#panel-home`, `link_to_examples`/`link_to_fileInput` wired via new `ui/tabs.ts` `showTab` (Bootstrap Tab API). New `src/style.css`: fullscreen-canvas + panel-overlay layout replacing v2 positioning hacks (`#panel-main-view` kept invisible so the canvas shows through), `#info`, `#logo1`. Images copied to `frontend/public/images/`. Verified live (links switch to File/Help panes). Footer → separate task. - [x] Migrate File panel (`views/file.R` → `src/ui/file.ts`) — Upload Network (`api.uploadNetwork`→`loadNetwork`), Load Session (`api.importSession`→`loadSession`), Save Session (`exportSession`), Load Example (fetches bundled `public/example_network.tsv` = v2 aspirin 3-channel, uploads it). Node/edge attribute inputs shown **disabled** (deferred to `/api/attributes`, Phase 7). `#file_status` line shows success/error. Verified live (Load Example → backend build → 2 layers, 11 nodes in `ctx`). -- [ ] Migrate Layer Selection & Layouts panel (`views/layouts.R` → `src/ui/layouts.ts`) +- [x] Migrate Layer Selection & Layouts panel (`views/layouts.R` → `src/ui/layouts.ts`) — subgraph-scope radios, Select/Deselect All, per-layer checkbox group (select/Hide/Labels → `selectLayer`/`setLayerVisibility`/`setLayerNodeLabels`, rebuilt on `network:loaded`), channel layout list (all checked by default), layout/clustering/local-layout selects + Run (`applyLayout`; local algorithm used for `nodesPerLayers` scope), topology metric select (from `config.topology_metrics`) + Run (`applyTopology`). `#layouts_status` shows success/error. v2 hide-button collapse dropped (panels are Bootstrap tabs now). - [ ] Migrate Scene Actions panel (`views/scene.R` → `src/ui/scene.ts`) - [ ] Migrate Layer Actions panel (`views/layer.R` → `src/ui/layer.ts`) - [ ] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) diff --git a/frontend/src/main.ts b/frontend/src/main.ts index f903286..ba48a59 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -9,6 +9,7 @@ import './style.css' import { fetchConfig } from './api/config' import { initHomePanel } from './ui/home' import { initFilePanel } from './ui/file' +import { initLayoutsPanel } from './ui/layouts' import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' @@ -45,6 +46,7 @@ async function main(): Promise { initHomePanel() initFilePanel() + initLayoutsPanel() registerGlobalListeners() registerThemeListener() registerLabelRendering() diff --git a/frontend/src/ui/layouts.ts b/frontend/src/ui/layouts.ts new file mode 100644 index 0000000..d6772ce --- /dev/null +++ b/frontend/src/ui/layouts.ts @@ -0,0 +1,247 @@ +// Layer Selection & Layouts panel — port of v2 views/layouts.R, the layer +// checkbox group (www/js/object_actions/layer.js attachLayerCheckboxes) and +// the channel layout list (edge.js attachChannelLayoutList). + +import { store } from '../store' +import { bus } from '../bus' +import { ctx } from '../three' +import { applyLayout, applyTopology } from '../actions/layout' +import { + selectLayer, + selectAllLayers, + setLayerVisibility, + setLayerNodeLabels, +} from '../actions/layer' +import type { Scope } from '../api/client' + +// v2 views/layouts.R option lists (Davidson-Harel / GEM / Edge Betweenness +// were commented out there too). +const LAYOUT_ALGORITHMS = [ + '-', + 'Fruchterman-Reingold', + 'Reingold-Tilford', + 'Circle', + 'Grid', + 'Random', + 'DrL', + 'Graphopt', + 'Kamada-Kawai', + 'Large Graph Layout', + 'Multidimensional Scaling', + 'Sugiyama', +] +const CLUSTERING_ALGORITHMS = [ + '-', + 'Louvain', + 'Walktrap', + 'Fast Greedy', + 'Label Propagation', +] + +const options = (values: string[]) => + values.map((v) => ``).join('') + +const LAYOUTS_HTML = ` +
+
+ +
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+ +
+
+` + +function status(msg: string, isError = false): void { + const el = document.getElementById('layouts_status') + if (!el) return + el.textContent = msg + el.className = `small ${isError ? 'text-danger' : 'text-success'}` +} + +// v2 attachLayerCheckboxes: per layer a select checkbox (labelled with the +// layer name) plus Hide and Labels checkboxes. +function buildLayerCheckboxes(): void { + const container = document.getElementById('checkboxdiv') + if (!container) return + container.innerHTML = '' + ctx.layers.forEach((layer, i) => { + const row = document.createElement('div') + row.className = 'form-check-inline' + row.innerHTML = ` + + + + + + + ` + const [sel, hide, labels] = row.querySelectorAll('input') + sel.addEventListener('change', () => selectLayer(i, sel.checked)) + hide.addEventListener('change', () => setLayerVisibility(i, !hide.checked)) + labels.addEventListener('change', () => setLayerNodeLabels(i, labels.checked)) + container.appendChild(row) + }) +} + +// v2 attachChannelLayoutList, minus the collapse toggle — all channels +// checked by default; checked ones are sent as selected_channels. +function buildChannelList(): void { + const container = document.getElementById('channelColorLayoutDiv') + if (!container) return + container.innerHTML = '' + const channels = store.get().network?.channels ?? [] + if (channels.length === 0) return + const title = document.createElement('label') + title.className = 'form-label' + title.textContent = 'Select Channels for Layouts' + container.appendChild(title) + for (const ch of channels) { + const row = document.createElement('div') + row.className = 'form-check' + row.innerHTML = ` + + + ` + container.appendChild(row) + } +} + +function selectedChannels(): string[] | null { + const boxes = document.querySelectorAll( + '#channelColorLayoutDiv .channel_checkbox' + ) + if (boxes.length === 0) return null + return [...boxes].filter((b) => b.checked).map((b) => b.id.replace('checkbox_layout', '')) +} + +function currentScope(): Scope { + const checked = document.querySelector( + 'input[name="subgraphChoice"]:checked' + ) + return (checked?.value ?? 'perLayer') as Scope +} + +async function onRunLayout(): Promise { + const st = store.get() + if (!st.network) return status('Upload a network first.', true) + if (st.selectedLayers.length === 0) + return status('Select at least one layer.', true) + + const scope = currentScope() + const algorithm = + scope === 'nodesPerLayers' + ? (document.getElementById('localLayoutAlgorithmChoice') as HTMLSelectElement).value + : (document.getElementById('layoutAlgorithmChoice') as HTMLSelectElement).value + if (algorithm === '-') return status('Select a layout algorithm.', true) + + const clusteringAlg = ( + document.getElementById('clusteringAlgorithmChoice') as HTMLSelectElement + ).value + try { + await applyLayout({ + nodes: st.network.nodes, + edges: st.network.edges, + algorithm, + scope, + selected_layers: st.selectedLayers, + selected_nodes: scope === 'nodesPerLayers' ? st.selectedNodes : null, + selected_channels: selectedChannels(), + clustering: + clusteringAlg !== '-' + ? { algorithm: clusteringAlg, local_layout: algorithm } + : null, + }) + status('Layout applied.') + } catch (err) { + status(err instanceof Error ? err.message : 'Layout failed.', true) + } +} + +async function onRunTopology(): Promise { + const st = store.get() + if (!st.network) return status('Upload a network first.', true) + if (st.selectedLayers.length === 0) + return status('Select at least one layer.', true) + const metric = ( + document.getElementById('topologyScaleMetricChoice') as HTMLSelectElement + ).value + if (metric === '-') return status('Select a topology metric.', true) + try { + await applyTopology({ + nodes: st.network.nodes, + edges: st.network.edges, + metric, + scope: currentScope(), + selected_layers: st.selectedLayers, + selected_channels: selectedChannels(), + }) + status('Node scaling applied.') + } catch (err) { + status(err instanceof Error ? err.message : 'Topology scaling failed.', true) + } +} + +export function initLayoutsPanel(): void { + const pane = document.getElementById('panel-layouts') + if (!pane) return + pane.innerHTML = LAYOUTS_HTML + + const metricSel = document.getElementById( + 'topologyScaleMetricChoice' + ) as HTMLSelectElement + metricSel.innerHTML = options(['-', ...(store.get().config?.topology_metrics ?? [])]) + + document + .getElementById('selectAllLayersCheckbox') + ?.addEventListener('change', (e) => { + selectAllLayers((e.target as HTMLInputElement).checked) + buildLayerCheckboxes() + }) + document.getElementById('runLayout')?.addEventListener('click', () => { + void onRunLayout() + }) + document.getElementById('runTopologyScale')?.addEventListener('click', () => { + void onRunTopology() + }) + + bus.on('network:loaded', () => { + buildLayerCheckboxes() + buildChannelList() + status('') + }) +} From da2fa10ba56e4c464eb43d9c17506512f785a71f Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 15:42:24 +0100 Subject: [PATCH 037/166] frontend: Scene Actions panel port (views/scene.R) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coords toggle, auto-rotate (animate-hook Y spin), predefined-layout radios. VR button and dead sceneColorPicker div dropped. #info overlay set pointer-events:none — it blocked clicks on panel controls. Verified live via Playwright. Co-Authored-By: Claude Fable 5 --- PLAN.md | 2 +- frontend/src/main.ts | 2 ++ frontend/src/style.css | 1 + frontend/src/ui/scene.ts | 75 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 frontend/src/ui/scene.ts diff --git a/PLAN.md b/PLAN.md index 238c56c..102e240 100644 --- a/PLAN.md +++ b/PLAN.md @@ -189,7 +189,7 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Migrate Home panel (`views/home.R` → `src/ui/home.ts`) — static HTML into `#panel-home`, `link_to_examples`/`link_to_fileInput` wired via new `ui/tabs.ts` `showTab` (Bootstrap Tab API). New `src/style.css`: fullscreen-canvas + panel-overlay layout replacing v2 positioning hacks (`#panel-main-view` kept invisible so the canvas shows through), `#info`, `#logo1`. Images copied to `frontend/public/images/`. Verified live (links switch to File/Help panes). Footer → separate task. - [x] Migrate File panel (`views/file.R` → `src/ui/file.ts`) — Upload Network (`api.uploadNetwork`→`loadNetwork`), Load Session (`api.importSession`→`loadSession`), Save Session (`exportSession`), Load Example (fetches bundled `public/example_network.tsv` = v2 aspirin 3-channel, uploads it). Node/edge attribute inputs shown **disabled** (deferred to `/api/attributes`, Phase 7). `#file_status` line shows success/error. Verified live (Load Example → backend build → 2 layers, 11 nodes in `ctx`). - [x] Migrate Layer Selection & Layouts panel (`views/layouts.R` → `src/ui/layouts.ts`) — subgraph-scope radios, Select/Deselect All, per-layer checkbox group (select/Hide/Labels → `selectLayer`/`setLayerVisibility`/`setLayerNodeLabels`, rebuilt on `network:loaded`), channel layout list (all checked by default), layout/clustering/local-layout selects + Run (`applyLayout`; local algorithm used for `nodesPerLayers` scope), topology metric select (from `config.topology_metrics`) + Run (`applyTopology`). `#layouts_status` shows success/error. v2 hide-button collapse dropped (panels are Bootstrap tabs now). -- [ ] Migrate Scene Actions panel (`views/scene.R` → `src/ui/scene.ts`) +- [x] Migrate Scene Actions panel (`views/scene.R` → `src/ui/scene.ts`) — scene-coords toggle (`Scene.toggleCoords`), auto-rotate checkbox (steady Y spin via `registerAnimateHook`; v2 tied it to the unported canvas rotate buttons), predefined-layout radios → `applyPredefinedLayout` (applied on change, as v2's `ignoreInit`). VR button dropped (Phase 7). Background color picker included (v2 injected it from `on_page_load.js initializeColorPickers` → `setRendererColor`). Also `#info` made `pointer-events: none` — its fixed overlay swallowed clicks on panel controls beneath it. Verified live (coords hide, rotation starts/halts, all 4 predefined layouts move layer planes). - [ ] Migrate Layer Actions panel (`views/layer.R` → `src/ui/layer.ts`) - [ ] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) - [ ] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) diff --git a/frontend/src/main.ts b/frontend/src/main.ts index ba48a59..7eebdeb 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -10,6 +10,7 @@ import { fetchConfig } from './api/config' import { initHomePanel } from './ui/home' import { initFilePanel } from './ui/file' import { initLayoutsPanel } from './ui/layouts' +import { initScenePanel } from './ui/scene' import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' @@ -47,6 +48,7 @@ async function main(): Promise { initHomePanel() initFilePanel() initLayoutsPanel() + initScenePanel() registerGlobalListeners() registerThemeListener() registerLabelRendering() diff --git a/frontend/src/style.css b/frontend/src/style.css index eeb9ee1..43db0cd 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -33,6 +33,7 @@ z-index: 2; font-size: 12px; color: var(--bs-body-color); + pointer-events: none; /* passive hint text — must not block panel clicks */ } #logo1 { diff --git a/frontend/src/ui/scene.ts b/frontend/src/ui/scene.ts new file mode 100644 index 0000000..7a06dbe --- /dev/null +++ b/frontend/src/ui/scene.ts @@ -0,0 +1,75 @@ +// Scene Actions panel — port of v2 views/scene.R plus the scene color picker +// v2 injected from on_page_load.js initializeColorPickers. The VR button is +// dropped (functions/vr.R deleted, PLAN Phase 7). + +import { MathUtils } from 'three' +import { ctx } from '../three' +import { registerAnimateHook, setRendererColor } from '../actions/screen' +import { applyPredefinedLayout, type PredefinedLayout } from '../actions/layout' + +const SCENE_HTML = ` +
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+` + +export function initScenePanel(): void { + const pane = document.getElementById('panel-scene') + if (!pane) return + pane.innerHTML = SCENE_HTML + + document.getElementById('toggleSceneCoords')?.addEventListener('change', (e) => { + ctx.scene?.toggleCoords((e.target as HTMLInputElement).checked) + }) + + // ponytail: v2 auto-rotate kept the canvas-button rotation interval alive; + // those buttons aren't ported, so this is a steady Y spin in the render loop. + document.getElementById('autoRotateScene')?.addEventListener('change', (e) => { + if (ctx.scene) ctx.scene.autoRotate = (e.target as HTMLInputElement).checked + }) + registerAnimateHook(() => { + if (ctx.scene?.autoRotate) ctx.scene.rotateY(MathUtils.degToRad(0.5)) + }) + + document.getElementById('scene_color')?.addEventListener('change', (e) => { + setRendererColor((e.target as HTMLInputElement).value) + }) + + // v2 observeEvent(input$predefined_layout, ignoreInit = T): applied on + // change, not on the initially-selected value. + for (const radio of document.querySelectorAll( + 'input[name="predefined_layout"]' + )) { + radio.addEventListener('change', () => { + applyPredefinedLayout(radio.value as PredefinedLayout) + }) + } +} From f61e0880b2df8dcdd4b1eff5f6001af09e5da257 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 15:57:37 +0100 Subject: [PATCH 038/166] frontend: Layer Actions panel port (views/layer.R) Show-labels radios, layer coords/wireframe toggles, label-size and floor-opacity sliders, color-priority radios, floor color picker (picking flips priority to picker, as v2 repaintLayersFromPicker). Verified live via Playwright. Co-Authored-By: Claude Fable 5 --- PLAN.md | 2 +- frontend/src/main.ts | 2 + frontend/src/ui/layer.ts | 108 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 frontend/src/ui/layer.ts diff --git a/PLAN.md b/PLAN.md index 102e240..19e26a4 100644 --- a/PLAN.md +++ b/PLAN.md @@ -190,7 +190,7 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Migrate File panel (`views/file.R` → `src/ui/file.ts`) — Upload Network (`api.uploadNetwork`→`loadNetwork`), Load Session (`api.importSession`→`loadSession`), Save Session (`exportSession`), Load Example (fetches bundled `public/example_network.tsv` = v2 aspirin 3-channel, uploads it). Node/edge attribute inputs shown **disabled** (deferred to `/api/attributes`, Phase 7). `#file_status` line shows success/error. Verified live (Load Example → backend build → 2 layers, 11 nodes in `ctx`). - [x] Migrate Layer Selection & Layouts panel (`views/layouts.R` → `src/ui/layouts.ts`) — subgraph-scope radios, Select/Deselect All, per-layer checkbox group (select/Hide/Labels → `selectLayer`/`setLayerVisibility`/`setLayerNodeLabels`, rebuilt on `network:loaded`), channel layout list (all checked by default), layout/clustering/local-layout selects + Run (`applyLayout`; local algorithm used for `nodesPerLayers` scope), topology metric select (from `config.topology_metrics`) + Run (`applyTopology`). `#layouts_status` shows success/error. v2 hide-button collapse dropped (panels are Bootstrap tabs now). - [x] Migrate Scene Actions panel (`views/scene.R` → `src/ui/scene.ts`) — scene-coords toggle (`Scene.toggleCoords`), auto-rotate checkbox (steady Y spin via `registerAnimateHook`; v2 tied it to the unported canvas rotate buttons), predefined-layout radios → `applyPredefinedLayout` (applied on change, as v2's `ignoreInit`). VR button dropped (Phase 7). Background color picker included (v2 injected it from `on_page_load.js initializeColorPickers` → `setRendererColor`). Also `#info` made `pointer-events: none` — its fixed overlay swallowed clicks on panel controls beneath it. Verified live (coords hide, rotation starts/halts, all 4 predefined layouts move layer planes). -- [ ] Migrate Layer Actions panel (`views/layer.R` → `src/ui/layer.ts`) +- [x] Migrate Layer Actions panel (`views/layer.R` → `src/ui/layer.ts`) — show-labels radios (`showLayerLabels`), layer-coords + wireframe toggles, label-size (12–30) and floor-opacity (0–1) sliders, color-priority radios, floor color picker (v2 `initializeColorPickers` + `repaintLayersFromPicker`: picking a color flips priority to picker and repaints). Verified live (coords/wireframe/opacity on both layers, label divs resize+hide, floor color 777777→22cc88 and back on default priority). - [ ] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) - [ ] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) - [ ] Migrate View Data panel (`views/data.R` → `src/ui/data.ts`) diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 7eebdeb..bed9245 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -11,6 +11,7 @@ import { initHomePanel } from './ui/home' import { initFilePanel } from './ui/file' import { initLayoutsPanel } from './ui/layouts' import { initScenePanel } from './ui/scene' +import { initLayerPanel } from './ui/layer' import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' @@ -49,6 +50,7 @@ async function main(): Promise { initFilePanel() initLayoutsPanel() initScenePanel() + initLayerPanel() registerGlobalListeners() registerThemeListener() registerLabelRendering() diff --git a/frontend/src/ui/layer.ts b/frontend/src/ui/layer.ts new file mode 100644 index 0000000..bc8bdb9 --- /dev/null +++ b/frontend/src/ui/layer.ts @@ -0,0 +1,108 @@ +// Layer Actions panel — port of v2 views/layer.R plus the floor color picker +// v2 injected from on_page_load.js initializeColorPickers. + +import { LAYER_DEFAULT_COLOR } from '../three/constants' +import { showLayerLabels, resizeLayerLabels } from '../actions/labels' +import { + showLayerCoords, + showWireFrames, + setFloorOpacity, + setLayerColorPriority, + repaintLayers, +} from '../actions/layer' + +const LAYER_HTML = ` +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ + +
+
+ + +
+
+
+ + +
+
+` + +export function initLayerPanel(): void { + const pane = document.getElementById('panel-layer') + if (!pane) return + pane.innerHTML = LAYER_HTML + + for (const radio of document.querySelectorAll( + 'input[name="showLayerLabelsRadio"]' + )) { + radio.addEventListener('change', () => { + showLayerLabels(radio.value as 'all' | 'selected' | 'none') + }) + } + + document.getElementById('showLayerCoords')?.addEventListener('change', (e) => { + showLayerCoords((e.target as HTMLInputElement).checked) + }) + document.getElementById('showWireFrames')?.addEventListener('change', (e) => { + showWireFrames((e.target as HTMLInputElement).checked) + }) + document.getElementById('resizeLayerLabels')?.addEventListener('input', (e) => { + resizeLayerLabels(Number((e.target as HTMLInputElement).value)) + }) + document.getElementById('layerOpacity')?.addEventListener('input', (e) => { + setFloorOpacity(Number((e.target as HTMLInputElement).value)) + }) + + for (const radio of document.querySelectorAll( + 'input[name="layerColorPriorityRadio"]' + )) { + radio.addEventListener('change', () => { + setLayerColorPriority(radio.value as 'default' | 'picker') + }) + } + + // v2 repaintLayersFromPicker: picking a color switches priority to picker. + document.getElementById('floor_color')?.addEventListener('change', () => { + const picker = document.getElementById( + 'layerPriority_picker' + ) as HTMLInputElement + picker.checked = true + setLayerColorPriority('picker') + repaintLayers() + }) +} From 07252f752446806da9f87c8eb6676db967306e0e Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 16:01:47 +0100 Subject: [PATCH 039/166] frontend: Node Actions panel port (views/node.R) Select-all, show-labels radios, label-size slider, geometry radios (sphere/box/diamond/cone), color-priority radios, highlight-selected toggle, comma-separated node search (Enter selects, case-insensitive). Verified live via Playwright. Co-Authored-By: Claude Fable 5 --- PLAN.md | 2 +- frontend/src/main.ts | 2 + frontend/src/ui/node.ts | 131 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 frontend/src/ui/node.ts diff --git a/PLAN.md b/PLAN.md index 19e26a4..2d005c9 100644 --- a/PLAN.md +++ b/PLAN.md @@ -191,7 +191,7 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Migrate Layer Selection & Layouts panel (`views/layouts.R` → `src/ui/layouts.ts`) — subgraph-scope radios, Select/Deselect All, per-layer checkbox group (select/Hide/Labels → `selectLayer`/`setLayerVisibility`/`setLayerNodeLabels`, rebuilt on `network:loaded`), channel layout list (all checked by default), layout/clustering/local-layout selects + Run (`applyLayout`; local algorithm used for `nodesPerLayers` scope), topology metric select (from `config.topology_metrics`) + Run (`applyTopology`). `#layouts_status` shows success/error. v2 hide-button collapse dropped (panels are Bootstrap tabs now). - [x] Migrate Scene Actions panel (`views/scene.R` → `src/ui/scene.ts`) — scene-coords toggle (`Scene.toggleCoords`), auto-rotate checkbox (steady Y spin via `registerAnimateHook`; v2 tied it to the unported canvas rotate buttons), predefined-layout radios → `applyPredefinedLayout` (applied on change, as v2's `ignoreInit`). VR button dropped (Phase 7). Background color picker included (v2 injected it from `on_page_load.js initializeColorPickers` → `setRendererColor`). Also `#info` made `pointer-events: none` — its fixed overlay swallowed clicks on panel controls beneath it. Verified live (coords hide, rotation starts/halts, all 4 predefined layouts move layer planes). - [x] Migrate Layer Actions panel (`views/layer.R` → `src/ui/layer.ts`) — show-labels radios (`showLayerLabels`), layer-coords + wireframe toggles, label-size (12–30) and floor-opacity (0–1) sliders, color-priority radios, floor color picker (v2 `initializeColorPickers` + `repaintLayersFromPicker`: picking a color flips priority to picker and repaints). Verified live (coords/wireframe/opacity on both layers, label divs resize+hide, floor color 777777→22cc88 and back on default priority). -- [ ] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) +- [x] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) — select/deselect-all checkbox, show-labels radios (default Selected), label-size slider (5–15), geometry radios (sphere/box/diamond/cone → `setNodeShape`), color-priority radios (default/cluster), highlight-selected checkbox, comma-separated search textarea (Enter selects matching nodes case-insensitively, v2 `selectSearchedNodes`). Verified live (select-all=11, geometry swaps SphereGeometry↔Box/Octahedron/Cone, search "aspirin"→1 node, unknown term→no change, labels show/resize). - [ ] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) - [ ] Migrate View Data panel (`views/data.R` → `src/ui/data.ts`) - [ ] Migrate FPS panel (`views/fps.R` → `src/ui/fps.ts`) diff --git a/frontend/src/main.ts b/frontend/src/main.ts index bed9245..9dc6489 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -12,6 +12,7 @@ import { initFilePanel } from './ui/file' import { initLayoutsPanel } from './ui/layouts' import { initScenePanel } from './ui/scene' import { initLayerPanel } from './ui/layer' +import { initNodePanel } from './ui/node' import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' @@ -51,6 +52,7 @@ async function main(): Promise { initLayoutsPanel() initScenePanel() initLayerPanel() + initNodePanel() registerGlobalListeners() registerThemeListener() registerLabelRendering() diff --git a/frontend/src/ui/node.ts b/frontend/src/ui/node.ts new file mode 100644 index 0000000..60158ce --- /dev/null +++ b/frontend/src/ui/node.ts @@ -0,0 +1,131 @@ +// Node Actions panel — port of v2 views/node.R (search-bar Enter handling +// from on_page_load.js selectSearchedNodes). + +import { showNodeLabels, resizeNodeLabels } from '../actions/labels' +import { + selectAllNodes, + selectNodesByName, + setNodeShape, + setNodeColorPriority, + setNodeSelectedColorPriority, +} from '../actions/node' +import type { NodeGeometryType } from '../three/Node' + +const NODE_HTML = ` +
+
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+` + +export function initNodePanel(): void { + const pane = document.getElementById('panel-node') + if (!pane) return + pane.innerHTML = NODE_HTML + + document.getElementById('selectAllNodes')?.addEventListener('change', (e) => { + selectAllNodes((e.target as HTMLInputElement).checked) + }) + + for (const radio of document.querySelectorAll( + 'input[name="showNodeLabelsRadio"]' + )) { + radio.addEventListener('change', () => { + showNodeLabels(radio.value as 'all' | 'selected' | 'none') + }) + } + + document.getElementById('resizeNodeLabels')?.addEventListener('input', (e) => { + resizeNodeLabels(Number((e.target as HTMLInputElement).value)) + }) + + for (const radio of document.querySelectorAll( + 'input[name="nodeGeometryRadio"]' + )) { + radio.addEventListener('change', () => { + setNodeShape(radio.value as NodeGeometryType) + }) + } + + for (const radio of document.querySelectorAll( + 'input[name="nodeColorPriorityRadio"]' + )) { + radio.addEventListener('change', () => { + setNodeColorPriority(radio.value as 'default' | 'cluster') + }) + } + + document + .getElementById('nodeSelectedColorPriority') + ?.addEventListener('change', (e) => { + setNodeSelectedColorPriority((e.target as HTMLInputElement).checked) + }) + + const searchBar = document.getElementById('nodeSearchBar') as HTMLTextAreaElement + searchBar.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + e.preventDefault() // v2: bypass the newline + selectNodesByName(searchBar.value.replace(/\n/g, '')) + } + }) +} From 8854b2a012e83c6fd0dfbe73573dcf8aef7cb87e Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 16:05:44 +0100 Subject: [PATCH 040/166] frontend: Edge Actions panel port (views/edge.R + edges.R toggles) Direction + opacity-by-weight toggles that show/hide their sliders (functions/edges.R), arrow-size/opacity/curvature sliders, color- priority checkboxes, and the per-channel color + Hide list. Verified live via Playwright. edges.R kept for updateSelectedEdgesView (View Data panel). Co-Authored-By: Claude Fable 5 --- PLAN.md | 4 +- frontend/src/main.ts | 2 + frontend/src/ui/edge.ts | 148 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 frontend/src/ui/edge.ts diff --git a/PLAN.md b/PLAN.md index 2d005c9..e95da25 100644 --- a/PLAN.md +++ b/PLAN.md @@ -192,12 +192,12 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Migrate Scene Actions panel (`views/scene.R` → `src/ui/scene.ts`) — scene-coords toggle (`Scene.toggleCoords`), auto-rotate checkbox (steady Y spin via `registerAnimateHook`; v2 tied it to the unported canvas rotate buttons), predefined-layout radios → `applyPredefinedLayout` (applied on change, as v2's `ignoreInit`). VR button dropped (Phase 7). Background color picker included (v2 injected it from `on_page_load.js initializeColorPickers` → `setRendererColor`). Also `#info` made `pointer-events: none` — its fixed overlay swallowed clicks on panel controls beneath it. Verified live (coords hide, rotation starts/halts, all 4 predefined layouts move layer planes). - [x] Migrate Layer Actions panel (`views/layer.R` → `src/ui/layer.ts`) — show-labels radios (`showLayerLabels`), layer-coords + wireframe toggles, label-size (12–30) and floor-opacity (0–1) sliders, color-priority radios, floor color picker (v2 `initializeColorPickers` + `repaintLayersFromPicker`: picking a color flips priority to picker and repaints). Verified live (coords/wireframe/opacity on both layers, label divs resize+hide, floor color 777777→22cc88 and back on default priority). - [x] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) — select/deselect-all checkbox, show-labels radios (default Selected), label-size slider (5–15), geometry radios (sphere/box/diamond/cone → `setNodeShape`), color-priority radios (default/cluster), highlight-selected checkbox, comma-separated search textarea (Enter selects matching nodes case-insensitively, v2 `selectSearchedNodes`). Verified live (select-all=11, geometry swaps SphereGeometry↔Box/Octahedron/Cone, search "aspirin"→1 node, unknown term→no change, labels show/resize). -- [ ] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) +- [x] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) — direction toggle (shows arrow-size sliders), opacity-by-weight (hides manual opacity sliders — both show/hide behaviours ported from `functions/edges.R`), intra/inter arrow-size + edge-opacity + channel-curvature sliders, highlight-selected + loaded-edge-color-priority checkboxes, and the channel color/Hide list (`attachChannelEditList`, rebuilt on `network:loaded`). Verified live (defaults hide opacity+arrow sliders, toggles reveal them, curvature 18/8, channel color→#123456, channel Hide→visibility false). - [ ] Migrate View Data panel (`views/data.R` → `src/ui/data.ts`) - [ ] Migrate FPS panel (`views/fps.R` → `src/ui/fps.ts`) - [ ] Migrate Help panel (`views/help.R` → `src/ui/help.ts`) — 920 lines of mostly static HTML; port as an HTML fragment, not TS - [ ] Migrate footer (`views/footer.R` → static footer in `index.html`) -- [ ] Port edge-panel toggles from `functions/edges.R` into `src/ui/edge.ts`, then delete it +- [x] Port edge-panel toggles from `functions/edges.R` into `src/ui/edge.ts` (done with the Edge panel above: `handleEdgeWidthByWeightCheckbox`/`handleEdgeDirectionCheckbox` slider show/hide). `updateSelectedEdgesView` remains — it renders the Selected Edges data table; port it with the View Data panel, then delete `edges.R`. - [ ] Add Undo/Redo buttons wired to `CommandHistory` - [ ] Delete `views/`, `ui.R`, `server.R`, `www/arena3dweb.css` diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 9dc6489..e447123 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -13,6 +13,7 @@ import { initLayoutsPanel } from './ui/layouts' import { initScenePanel } from './ui/scene' import { initLayerPanel } from './ui/layer' import { initNodePanel } from './ui/node' +import { initEdgePanel } from './ui/edge' import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' @@ -53,6 +54,7 @@ async function main(): Promise { initScenePanel() initLayerPanel() initNodePanel() + initEdgePanel() registerGlobalListeners() registerThemeListener() registerLabelRendering() diff --git a/frontend/src/ui/edge.ts b/frontend/src/ui/edge.ts new file mode 100644 index 0000000..3a5f58d --- /dev/null +++ b/frontend/src/ui/edge.ts @@ -0,0 +1,148 @@ +// Edge Actions panel — port of v2 views/edge.R plus the show/hide slider +// toggles from functions/edges.R (handleEdgeWidthByWeightCheckbox, +// handleEdgeDirectionCheckbox) and the channel color/visibility list from +// www/js/object_actions/edge.js attachChannelEditList. + +import { ctx } from '../three' +import { bus } from '../bus' +import { store } from '../store' +import { + toggleDirection, + setIntraDirectionArrowSize, + setInterDirectionArrowSize, + setEdgeWidthByWeight, + setIntraLayerEdgeOpacity, + setInterLayerEdgeOpacity, + setIntraChannelCurvature, + setInterChannelCurvature, + setEdgeSelectedColorPriority, + setEdgeFileColorPriority, + setChannelColor, + setChannelVisibility, +} from '../actions/edge' + +const EDGE_HTML = ` +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+` + +function show(id: string, visible: boolean): void { + document.getElementById(id)?.classList.toggle('d-none', !visible) +} + +// v2 attachChannelEditList: per channel a color picker + a Hide checkbox. +function buildChannelEditList(): void { + const container = document.getElementById('channelColorPicker') + if (!container) return + container.innerHTML = '' + const channels = store.get().network?.channels ?? [] + if (channels.length === 0) return + + const title = document.createElement('h4') + title.textContent = 'Channels' + container.appendChild(title) + + for (const ch of channels) { + const row = document.createElement('div') + row.className = 'channel_subcontainer d-flex align-items-center gap-2 mb-2' + row.innerHTML = ` + ${ch}: + + + + ` + const picker = row.querySelector('.channel_colorPicker')! + const hide = row.querySelector('.channel_checkbox')! + picker.addEventListener('change', () => setChannelColor(ch, picker.value)) + hide.addEventListener('change', () => setChannelVisibility(ch, !hide.checked)) + container.appendChild(row) + } +} + +export function initEdgePanel(): void { + const pane = document.getElementById('panel-edge') + if (!pane) return + pane.innerHTML = EDGE_HTML + + const range = (id: string, fn: (v: number) => void) => + document.getElementById(id)?.addEventListener('input', (e) => { + fn(Number((e.target as HTMLInputElement).value)) + }) + + // Direction toggle shows/hides the arrow-size sliders (edges.R). + document.getElementById('edgeDirectionToggle')?.addEventListener('change', (e) => { + const on = (e.target as HTMLInputElement).checked + show('intraDirectionArrowSizeWrap', on) + show('interDirectionArrowSizeWrap', on) + toggleDirection(on) + }) + range('intraDirectionArrowSize', setIntraDirectionArrowSize) + range('interDirectionArrowSize', setInterDirectionArrowSize) + + // Opacity-by-weight hides the manual opacity sliders (edges.R). + document.getElementById('edgeWidthByWeight')?.addEventListener('change', (e) => { + const byWeight = (e.target as HTMLInputElement).checked + show('intraLayerEdgeOpacityWrap', !byWeight) + show('interLayerEdgeOpacityWrap', !byWeight) + setEdgeWidthByWeight(byWeight) + }) + range('intraLayerEdgeOpacity', setIntraLayerEdgeOpacity) + range('interLayerEdgeOpacity', setInterLayerEdgeOpacity) + + range('intraChannelCurvature', setIntraChannelCurvature) + range('interChannelCurvature', setInterChannelCurvature) + + document + .getElementById('edgeSelectedColorPriority') + ?.addEventListener('change', (e) => { + setEdgeSelectedColorPriority((e.target as HTMLInputElement).checked) + }) + document + .getElementById('edgeFileColorPriority') + ?.addEventListener('change', (e) => { + setEdgeFileColorPriority((e.target as HTMLInputElement).checked) + }) + + bus.on('network:loaded', buildChannelEditList) +} From 13e0e808bddb1ada896d2ef581557c9248f88c2a Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 16:07:36 +0100 Subject: [PATCH 041/166] frontend: FPS panel port (views/fps.R) 15/30/60 FPS radios set ctx.fps (read by the animate render loop). Default checked reflects the real ctx.fps. Verified live via Playwright. Co-Authored-By: Claude Fable 5 --- PLAN.md | 2 +- frontend/src/main.ts | 2 ++ frontend/src/ui/fps.ts | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 frontend/src/ui/fps.ts diff --git a/PLAN.md b/PLAN.md index e95da25..b01ec0a 100644 --- a/PLAN.md +++ b/PLAN.md @@ -194,7 +194,7 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) — select/deselect-all checkbox, show-labels radios (default Selected), label-size slider (5–15), geometry radios (sphere/box/diamond/cone → `setNodeShape`), color-priority radios (default/cluster), highlight-selected checkbox, comma-separated search textarea (Enter selects matching nodes case-insensitively, v2 `selectSearchedNodes`). Verified live (select-all=11, geometry swaps SphereGeometry↔Box/Octahedron/Cone, search "aspirin"→1 node, unknown term→no change, labels show/resize). - [x] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) — direction toggle (shows arrow-size sliders), opacity-by-weight (hides manual opacity sliders — both show/hide behaviours ported from `functions/edges.R`), intra/inter arrow-size + edge-opacity + channel-curvature sliders, highlight-selected + loaded-edge-color-priority checkboxes, and the channel color/Hide list (`attachChannelEditList`, rebuilt on `network:loaded`). Verified live (defaults hide opacity+arrow sliders, toggles reveal them, curvature 18/8, channel color→#123456, channel Hide→visibility false). - [ ] Migrate View Data panel (`views/data.R` → `src/ui/data.ts`) -- [ ] Migrate FPS panel (`views/fps.R` → `src/ui/fps.ts`) +- [x] Migrate FPS panel (`views/fps.R` → `src/ui/fps.ts`) — 15/30/60 radios set `ctx.fps` (read by the `animate` loop). Default checked reflects the real `ctx.fps` (30) rather than v2's UI default of 15. Verified live (30 checked, switches to 60/15). - [ ] Migrate Help panel (`views/help.R` → `src/ui/help.ts`) — 920 lines of mostly static HTML; port as an HTML fragment, not TS - [ ] Migrate footer (`views/footer.R` → static footer in `index.html`) - [x] Port edge-panel toggles from `functions/edges.R` into `src/ui/edge.ts` (done with the Edge panel above: `handleEdgeWidthByWeightCheckbox`/`handleEdgeDirectionCheckbox` slider show/hide). `updateSelectedEdgesView` remains — it renders the Selected Edges data table; port it with the View Data panel, then delete `edges.R`. diff --git a/frontend/src/main.ts b/frontend/src/main.ts index e447123..3733f33 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -14,6 +14,7 @@ import { initScenePanel } from './ui/scene' import { initLayerPanel } from './ui/layer' import { initNodePanel } from './ui/node' import { initEdgePanel } from './ui/edge' +import { initFpsPanel } from './ui/fps' import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' @@ -55,6 +56,7 @@ async function main(): Promise { initLayerPanel() initNodePanel() initEdgePanel() + initFpsPanel() registerGlobalListeners() registerThemeListener() registerLabelRendering() diff --git a/frontend/src/ui/fps.ts b/frontend/src/ui/fps.ts new file mode 100644 index 0000000..80b8718 --- /dev/null +++ b/frontend/src/ui/fps.ts @@ -0,0 +1,30 @@ +// FPS panel — port of v2 views/fps.R. Sets ctx.fps, which the render loop +// (screen.ts animate) reads each frame. Default checked = current ctx.fps. + +import { ctx } from '../three' + +const FPS_HTML = ` +
+ + ${[15, 30, 60] + .map( + (f) => ` +
+ + +
` + ) + .join('')} +
+` + +export function initFpsPanel(): void { + const pane = document.getElementById('panel-fps') + if (!pane) return + pane.innerHTML = FPS_HTML + for (const radio of document.querySelectorAll('input[name="fps"]')) { + radio.addEventListener('change', () => { + ctx.fps = Number(radio.value) || 30 + }) + } +} From f3891e769e0b1c02384fef6a9fd33c7ed1b53aa3 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 16:34:23 +0100 Subject: [PATCH 042/166] frontend: View Data panel port (views/data.R + render.R tables) Port views/data.R to src/ui/data.ts: tabsetPanel of Network Data, Selected Edges, and per-metric (Degree/Clustering Coefficient/ Betweenness Centrality) + Clustering Data tables, replacing DT datatables with plain HTML tables + CSV download. Wire table population to bus events (network:loaded, clustering:applied, topology:applied) and recompute Selected Edges from ctx on panel open (port of functions/edges.R updateSelectedEdgesView). Metric tabs stay hidden until their event fires (v2 hideDataMetricTabs), and Selected Edges hides again when no edge is selected. Add `metric` to the topology:applied event payload and thread it through ApplyTopologyCommand so the panel routes each result to the right tab. Co-Authored-By: Claude Fable 5 --- frontend/src/actions/layout.ts | 2 +- frontend/src/bus/index.ts | 2 +- frontend/src/commands/scene.test.ts | 2 +- frontend/src/commands/scene.ts | 7 +- frontend/src/main.ts | 2 + frontend/src/ui/data.ts | 198 ++++++++++++++++++++++++++++ 6 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 frontend/src/ui/data.ts diff --git a/frontend/src/actions/layout.ts b/frontend/src/actions/layout.ts index 89b3197..3062988 100644 --- a/frontend/src/actions/layout.ts +++ b/frontend/src/actions/layout.ts @@ -58,7 +58,7 @@ export async function applyLayout(req: LayoutRequest): Promise { export async function applyTopology(req: TopologyRequest): Promise { // scales are already mapped into the target range server-side (SPEC §6). const res = await api.topology(req) - history.execute(new ApplyTopologyCommand(res.scales)) + history.execute(new ApplyTopologyCommand(req.metric, res.scales)) } // Predefined layer arrangements (v2 applyPredefinedLayout). Direct layer diff --git a/frontend/src/bus/index.ts b/frontend/src/bus/index.ts index a710249..ace7c58 100644 --- a/frontend/src/bus/index.ts +++ b/frontend/src/bus/index.ts @@ -8,7 +8,7 @@ export interface BusEvents { 'edge:color-changed': { edgeId: string; color: string } 'layout:applied': { positions: Record } 'clustering:applied': { clusters: Record } - 'topology:applied': { scales: Record } + 'topology:applied': { metric: string; scales: Record } 'layer:moved': { layerIndex: number } 'theme:changed': { theme: string } 'history:changed': { canUndo: boolean; canRedo: boolean } diff --git a/frontend/src/commands/scene.test.ts b/frontend/src/commands/scene.test.ts index 5ed5a68..c3de6ff 100644 --- a/frontend/src/commands/scene.test.ts +++ b/frontend/src/commands/scene.test.ts @@ -121,7 +121,7 @@ describe('ApplyLayoutCommand', () => { describe('ApplyTopologyCommand', () => { it('scales nodes and restores', () => { - const c = new ApplyTopologyCommand({ 'A::L': 2.5, 'B::L': 0.5 }) + const c = new ApplyTopologyCommand('Degree', { 'A::L': 2.5, 'B::L': 0.5 }) c.execute() expect(ctx.nodeObjects[0].getScale()).toBe(2.5) expect(ctx.nodeObjects[1].getScale()).toBe(0.5) diff --git a/frontend/src/commands/scene.ts b/frontend/src/commands/scene.ts index fb8fbdf..f7e5ad4 100644 --- a/frontend/src/commands/scene.ts +++ b/frontend/src/commands/scene.ts @@ -238,7 +238,10 @@ export class ApplyTopologyCommand implements Command { description = 'Apply topology metric' private readonly before: Record = {} - constructor(private readonly scales: Record) { + constructor( + private readonly metric: string, + private readonly scales: Record + ) { for (const name of Object.keys(scales)) this.before[name] = ctx.nodeObjects[ctx.nodeLayerNames.indexOf(name)].getScale() @@ -247,7 +250,7 @@ export class ApplyTopologyCommand implements Command { private apply(scales: Record): void { for (const [name, s] of Object.entries(scales)) ctx.nodeObjects[ctx.nodeLayerNames.indexOf(name)].setScale(s) - bus.emit('topology:applied', { scales }) + bus.emit('topology:applied', { metric: this.metric, scales }) } execute(): void { diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 3733f33..434111d 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -15,6 +15,7 @@ import { initLayerPanel } from './ui/layer' import { initNodePanel } from './ui/node' import { initEdgePanel } from './ui/edge' import { initFpsPanel } from './ui/fps' +import { initDataPanel } from './ui/data' import { store } from './store' import { ctx, Scene } from './three' import { history } from './commands/base' @@ -57,6 +58,7 @@ async function main(): Promise { initNodePanel() initEdgePanel() initFpsPanel() + initDataPanel() registerGlobalListeners() registerThemeListener() registerLabelRendering() diff --git a/frontend/src/ui/data.ts b/frontend/src/ui/data.ts new file mode 100644 index 0000000..dc6a9f5 --- /dev/null +++ b/frontend/src/ui/data.ts @@ -0,0 +1,198 @@ +// View Data panel — port of v2 views/data.R plus its table renderers: +// functions/render.R (renderNetworkDF / renderClusteringDF / renderMetricTable, +// hideDataMetricTabs) and functions/edges.R updateSelectedEdgesView. +// +// v2 rendered DT datatables (excel/csv/copy/pdf/print buttons, per-column +// filters). Ported as plain HTML tables with a CSV download button. +// ponytail: no per-column filters or excel/pdf/print export, add if missed. + +import { Tab } from 'bootstrap' +import { bus } from '../bus' +import { store } from '../store' +import { ctx } from '../three' + +const TABS = [ + { id: 'network', label: 'Network Data' }, + { id: 'selectedEdges', label: 'Selected Edges' }, + { id: 'clustering', label: 'Clustering Data' }, + { id: 'degree', label: 'Degree' }, + { id: 'transitivity', label: 'Clustering Coefficient' }, + { id: 'betweenness', label: 'Betweenness Centrality' }, +] as const + +// v2 renderMetricTable keyed output ids by metric (init.R TOPOLOGY_METRICS) +const METRIC_TAB: Record = { + Degree: 'degree', + 'Clustering Coefficient': 'transitivity', + 'Betweenness Centrality': 'betweenness', +} + +const DATA_HTML = ` + +
+ ${TABS.map( + (t) => + `
` + ).join('')} +
+` + +function esc(v: string | number): string { + return String(v) + .replace(/&/g, '&') + .replace(//g, '>') +} + +function toCsv(columns: string[], rows: (string | number)[][]): string { + const q = (v: string | number) => { + const s = String(v) + return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s + } + return [columns, ...rows].map((r) => r.map(q).join(',')).join('\n') +} + +function activateDataTab(tabId: string): void { + const btn = document.querySelector(`[data-bs-target="#dataPane-${tabId}"]`) + if (btn) Tab.getOrCreateInstance(btn).show() +} + +/** v2 renderShinyDataTable + showTab: fill a pane, unhide its tab, focus it. */ +function renderTable( + tabId: string, + columns: string[], + rows: (string | number)[][], + fileName: string, + focus = true +): void { + const pane = document.getElementById(`dataPane-${tabId}`) + if (!pane) return + pane.innerHTML = ` + +
+ + ${columns.map((c) => ``).join('')} + ${rows + .map((r) => `${r.map((v) => ``).join('')}`) + .join('')} +
${esc(c)}
${esc(v)}
+
` + document.getElementById(`dataCsv-${tabId}`)?.addEventListener('click', () => { + const url = URL.createObjectURL( + new Blob([toCsv(columns, rows)], { type: 'text/csv' }) + ) + const a = document.createElement('a') + a.href = url + a.download = `${fileName}.csv` + a.click() + URL.revokeObjectURL(url) + }) + document.getElementById(`dataTab-${tabId}`)?.classList.remove('d-none') + if (focus) activateDataTab(tabId) +} + +function renderNetworkTable(): void { + const network = store.get().network + if (!network) return + const hasChannels = network.channels.length > 0 + const columns = [ + 'SourceNode', + 'SourceLayer', + 'TargetNode', + 'TargetLayer', + 'Weight', + ] + if (hasChannels) columns.push('Channel') + const rows = network.edges.map((e) => { + const row: (string | number)[] = [ + e.source_node, + e.source_layer, + e.target_node, + e.target_layer, + e.weight, + ] + if (hasChannels) row.push(e.channel ?? '') + return row + }) + renderTable('network', columns, rows, 'networkData') +} + +/** v2 updateSelectedEdgesView: per-channel rows; tab hidden when none selected. */ +function refreshSelectedEdges(): void { + const rows: (string | number)[][] = [] + for (const e of ctx.edgeObjects) { + if (!e.isSelected) continue + const base = [ + ctx.nodeObjects[e.sourceNodeIndex].name, + ctx.layers[e.sourceLayerIndex].name, + ctx.nodeObjects[e.targetNodeIndex].name, + ctx.layers[e.targetLayerIndex].name, + ] + if (e.channels.length > 0) + e.channels.forEach((ch, j) => rows.push([...base, ch, e.weights[j]])) + else rows.push([...base, '', e.weights[0] ?? '']) + } + if (rows.length > 0) { + renderTable( + 'selectedEdges', + [ + 'SourceNode', + 'SourceLayer', + 'TargetNode', + 'TargetLayer', + 'Channel', + 'Weight', + ], + rows, + 'selectedEdgeData', + false + ) + } else { + const item = document.getElementById('dataTab-selectedEdges') + if (item && !item.classList.contains('d-none')) { + const wasActive = item + .querySelector('.nav-link') + ?.classList.contains('active') + item.classList.add('d-none') + if (wasActive) activateDataTab('network') + } + } +} + +export function initDataPanel(): void { + const pane = document.getElementById('panel-data') + if (!pane) return + pane.innerHTML = DATA_HTML + + bus.on('network:loaded', () => renderNetworkTable()) + bus.on('clustering:applied', ({ clusters }) => { + renderTable( + 'clustering', + ['Node', 'Cluster'], + Object.entries(clusters), + 'clusteringData' + ) + }) + bus.on('topology:applied', ({ metric, scales }) => { + const tabId = METRIC_TAB[metric] + if (tabId) + renderTable( + tabId, + ['Node', 'Scale'], + Object.entries(scales), + `${tabId}Data` + ) + }) + // Selected edges have no bus event (selection is pure ctx state); recompute + // whenever the View Data panel is opened. + document + .querySelector('[data-bs-target="#panel-data"]') + ?.addEventListener('shown.bs.tab', () => refreshSelectedEdges()) +} From 64c24fc0b48d3a9e2b38c86b8fd3a41883b23a36 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 16:35:11 +0100 Subject: [PATCH 043/166] docs: mark View Data panel + edges.R done in PLAN/MIGRATION Co-Authored-By: Claude Fable 5 --- MIGRATION.md | 4 ++-- PLAN.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index cca6f4e..25b5175 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -23,7 +23,7 @@ See `PLAN.md` for the ordered implementation steps. | `functions/reset.R` | *(stateless server — no equivalent needed)* | [x] | | `functions/render.R` | *(absorbed into FastAPI error responses)* | [x] | | `functions/js_handling.R` | *(absorbed into frontend EventBus)* | [x] | -| `functions/edges.R` | `frontend/src/ui/edge.ts` — UI visibility toggles + JS handler calls, no server logic | [ ] | +| `functions/edges.R` | `frontend/src/ui/edge.ts` (toggles) + `frontend/src/ui/data.ts` (`updateSelectedEdgesView`) | [x] | | `functions/vr.R` | **DROPPED** — VR mode not ported (depended on external `bib.fleming.gr` hosting + A-Frame; niche, out of scope) | [x] | | `functions/igraph/general.R` | `backend/app/services/graph.py` | [x] | | `functions/igraph/layout.R` | `backend/app/services/layouts.py` | [x] | @@ -89,7 +89,7 @@ See `PLAN.md` for the ordered implementation steps. | `views/layer.R` | `frontend/src/ui/layer.ts` | [ ] | | `views/node.R` | `frontend/src/ui/node.ts` | [ ] | | `views/edge.R` | `frontend/src/ui/edge.ts` | [ ] | -| `views/data.R` | `frontend/src/ui/data.ts` | [ ] | +| `views/data.R` | `frontend/src/ui/data.ts` | [x] | | `views/fps.R` | `frontend/src/ui/fps.ts` | [ ] | | `views/help.R` | `frontend/src/ui/help.ts` | [ ] | | `views/footer.R` | `frontend/index.html` (static footer) | [ ] | diff --git a/PLAN.md b/PLAN.md index b01ec0a..d03b277 100644 --- a/PLAN.md +++ b/PLAN.md @@ -193,11 +193,11 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api - [x] Migrate Layer Actions panel (`views/layer.R` → `src/ui/layer.ts`) — show-labels radios (`showLayerLabels`), layer-coords + wireframe toggles, label-size (12–30) and floor-opacity (0–1) sliders, color-priority radios, floor color picker (v2 `initializeColorPickers` + `repaintLayersFromPicker`: picking a color flips priority to picker and repaints). Verified live (coords/wireframe/opacity on both layers, label divs resize+hide, floor color 777777→22cc88 and back on default priority). - [x] Migrate Node Actions panel (`views/node.R` → `src/ui/node.ts`) — select/deselect-all checkbox, show-labels radios (default Selected), label-size slider (5–15), geometry radios (sphere/box/diamond/cone → `setNodeShape`), color-priority radios (default/cluster), highlight-selected checkbox, comma-separated search textarea (Enter selects matching nodes case-insensitively, v2 `selectSearchedNodes`). Verified live (select-all=11, geometry swaps SphereGeometry↔Box/Octahedron/Cone, search "aspirin"→1 node, unknown term→no change, labels show/resize). - [x] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) — direction toggle (shows arrow-size sliders), opacity-by-weight (hides manual opacity sliders — both show/hide behaviours ported from `functions/edges.R`), intra/inter arrow-size + edge-opacity + channel-curvature sliders, highlight-selected + loaded-edge-color-priority checkboxes, and the channel color/Hide list (`attachChannelEditList`, rebuilt on `network:loaded`). Verified live (defaults hide opacity+arrow sliders, toggles reveal them, curvature 18/8, channel color→#123456, channel Hide→visibility false). -- [ ] Migrate View Data panel (`views/data.R` → `src/ui/data.ts`) +- [x] Migrate View Data panel (`views/data.R` → `src/ui/data.ts`) — 6 sub-tabs (Network Data, Selected Edges, Clustering Data, Degree, Clustering Coefficient, Betweenness Centrality) as plain HTML tables with CSV download, replacing v2 DT datatables. Network table fills on `network:loaded`; Clustering/metric tables on `clustering:applied`/`topology:applied` (added `metric` to the topology event payload + `ApplyTopologyCommand` so results route to the right tab); Selected Edges recomputed from `ctx` on panel open (port of `functions/edges.R updateSelectedEdgesView`). Metric tabs hidden until their event fires (v2 `hideDataMetricTabs`), Selected Edges re-hides when nothing selected. Verified live (Network Data 21 aspirin rows, Degree tab appears after run w/ layers selected, Selected Edges 3 channel-rows on edge select then hides on deselect). - [x] Migrate FPS panel (`views/fps.R` → `src/ui/fps.ts`) — 15/30/60 radios set `ctx.fps` (read by the `animate` loop). Default checked reflects the real `ctx.fps` (30) rather than v2's UI default of 15. Verified live (30 checked, switches to 60/15). - [ ] Migrate Help panel (`views/help.R` → `src/ui/help.ts`) — 920 lines of mostly static HTML; port as an HTML fragment, not TS - [ ] Migrate footer (`views/footer.R` → static footer in `index.html`) -- [x] Port edge-panel toggles from `functions/edges.R` into `src/ui/edge.ts` (done with the Edge panel above: `handleEdgeWidthByWeightCheckbox`/`handleEdgeDirectionCheckbox` slider show/hide). `updateSelectedEdgesView` remains — it renders the Selected Edges data table; port it with the View Data panel, then delete `edges.R`. +- [x] Port edge-panel toggles from `functions/edges.R` into `src/ui/edge.ts` (done with the Edge panel above: `handleEdgeWidthByWeightCheckbox`/`handleEdgeDirectionCheckbox` slider show/hide). `updateSelectedEdgesView` ported into `src/ui/data.ts` (recomputed from `ctx` on View Data panel open). `functions/edges.R` fully ported; delete in Phase 14 cleanup. - [ ] Add Undo/Redo buttons wired to `CommandHistory` - [ ] Delete `views/`, `ui.R`, `server.R`, `www/arena3dweb.css` From 6390053446876661ff22c6e4ab531ae43a026a54 Mon Sep 17 00:00:00 2001 From: vagkaratzas Date: Fri, 3 Jul 2026 21:45:22 +0100 Subject: [PATCH 044/166] =?UTF-8?q?frontend:=20Help=20panel=20port=20(view?= =?UTF-8?q?s/help.R=20=E2=86=92=20src/ui/help.ts)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port the 12-tab Help page as a static HTML fragment with data-attribute-driven inner tab switching (openHelpTab), Examples open by default. Copy help images and example datasets into frontend/public/{images/help,data}; rewrite ./ asset paths to /, add max-width:100% to images and horizontal scroll to
 blocks.
Port the help CSS rules into style.css scoped under #helpDiv. Wire initHelpPanel
into main.ts. Verified live: 12 tabs, Examples↔API switching, 19/19 images load.

Co-Authored-By: Claude Fable 5 
---
 PLAN.md                                       |     2 +-
 .../public/data/Arena3DwebApp_aspirin.json    |   751 +
 frontend/public/data/GPCRs_data.tsv           |   333 +
 .../data/GPCRs_data_edge_attributes.tsv       |   159 +
 .../data/GPCRs_data_node_attributes.tsv       |    11 +
 frontend/public/data/GPCRs_export.json        |  3083 ++
 frontend/public/data/StringApp_aspirin.cys    |   Bin 0 -> 1080142 bytes
 frontend/public/data/aspirin_3channels.tsv    |    40 +
 .../data/aspirin_3channels_directed.json      |   437 +
 frontend/public/data/covid19_data.tsv         |   986 +
 .../data/covid19_data_edge_attributes.tsv     |    56 +
 .../data/covid19_data_node_attributes.tsv     |    65 +
 frontend/public/data/covid19_export.json      | 11980 ++++++
 frontend/public/data/figure1_data.tsv         |  2397 ++
 frontend/public/data/figure1_export.json      | 34462 ++++++++++++++++
 frontend/public/data/figure2A_data.tsv        |   439 +
 frontend/public/data/figure2A_export.json     |  5930 +++
 frontend/public/data/figure2B_data.tsv        |   427 +
 .../data/figure2B_data_edge_attributes.tsv    |    75 +
 .../data/figure2B_data_node_attributes.tsv    |     5 +
 frontend/public/data/figure2B_export.json     |  5853 +++
 frontend/public/data/prego.json               |   601 +
 frontend/public/data/prego.tsv                |    32 +
 frontend/public/data/transpose.py             |    22 +
 frontend/public/images/help/arena3dwebapp.png |   Bin 0 -> 67523 bytes
 .../images/help/arena_cytoscape_aspirin.png   |   Bin 0 -> 136329 bytes
 frontend/public/images/help/channels.png      |   Bin 0 -> 88741 bytes
 frontend/public/images/help/clustering.png    |   Bin 0 -> 2700070 bytes
 .../public/images/help/cytoscape_aspirin.png  |   Bin 0 -> 348759 bytes
 .../public/images/help/directed_graph.png     |   Bin 0 -> 559691 bytes
 frontend/public/images/help/edges.png         |   Bin 0 -> 48125 bytes
 frontend/public/images/help/file.png          |   Bin 0 -> 20815 bytes
 frontend/public/images/help/fps.PNG           |   Bin 0 -> 1758 bytes
 frontend/public/images/help/fps2.PNG          |   Bin 0 -> 4533 bytes
 frontend/public/images/help/layers.png        |   Bin 0 -> 26435 bytes
 frontend/public/images/help/layouts.png       |   Bin 0 -> 20961 bytes
 .../public/images/help/layouts_figure.png     |   Bin 0 -> 5416014 bytes
 frontend/public/images/help/mainview.png      |   Bin 0 -> 122084 bytes
 .../public/images/help/navigation_panel.png   |   Bin 0 -> 101912 bytes
 frontend/public/images/help/nodes.png         |   Bin 0 -> 15110 bytes
 .../public/images/help/predefined_layouts.png |   Bin 0 -> 639101 bytes
 frontend/public/images/help/scene1_1.PNG      |   Bin 0 -> 17078 bytes
 frontend/public/images/help/themes.png        |   Bin 0 -> 5565966 bytes
 frontend/public/images/help/vr.png            |   Bin 0 -> 7224882 bytes
 frontend/src/main.ts                          |     2 +
 frontend/src/style.css                        |    59 +
 frontend/src/ui/help.ts                       |   738 +
 47 files changed, 68944 insertions(+), 1 deletion(-)
 create mode 100644 frontend/public/data/Arena3DwebApp_aspirin.json
 create mode 100644 frontend/public/data/GPCRs_data.tsv
 create mode 100644 frontend/public/data/GPCRs_data_edge_attributes.tsv
 create mode 100644 frontend/public/data/GPCRs_data_node_attributes.tsv
 create mode 100644 frontend/public/data/GPCRs_export.json
 create mode 100644 frontend/public/data/StringApp_aspirin.cys
 create mode 100644 frontend/public/data/aspirin_3channels.tsv
 create mode 100644 frontend/public/data/aspirin_3channels_directed.json
 create mode 100644 frontend/public/data/covid19_data.tsv
 create mode 100644 frontend/public/data/covid19_data_edge_attributes.tsv
 create mode 100644 frontend/public/data/covid19_data_node_attributes.tsv
 create mode 100644 frontend/public/data/covid19_export.json
 create mode 100644 frontend/public/data/figure1_data.tsv
 create mode 100644 frontend/public/data/figure1_export.json
 create mode 100644 frontend/public/data/figure2A_data.tsv
 create mode 100644 frontend/public/data/figure2A_export.json
 create mode 100644 frontend/public/data/figure2B_data.tsv
 create mode 100644 frontend/public/data/figure2B_data_edge_attributes.tsv
 create mode 100644 frontend/public/data/figure2B_data_node_attributes.tsv
 create mode 100644 frontend/public/data/figure2B_export.json
 create mode 100644 frontend/public/data/prego.json
 create mode 100644 frontend/public/data/prego.tsv
 create mode 100644 frontend/public/data/transpose.py
 create mode 100644 frontend/public/images/help/arena3dwebapp.png
 create mode 100644 frontend/public/images/help/arena_cytoscape_aspirin.png
 create mode 100644 frontend/public/images/help/channels.png
 create mode 100644 frontend/public/images/help/clustering.png
 create mode 100644 frontend/public/images/help/cytoscape_aspirin.png
 create mode 100644 frontend/public/images/help/directed_graph.png
 create mode 100644 frontend/public/images/help/edges.png
 create mode 100644 frontend/public/images/help/file.png
 create mode 100644 frontend/public/images/help/fps.PNG
 create mode 100644 frontend/public/images/help/fps2.PNG
 create mode 100644 frontend/public/images/help/layers.png
 create mode 100644 frontend/public/images/help/layouts.png
 create mode 100644 frontend/public/images/help/layouts_figure.png
 create mode 100644 frontend/public/images/help/mainview.png
 create mode 100644 frontend/public/images/help/navigation_panel.png
 create mode 100644 frontend/public/images/help/nodes.png
 create mode 100644 frontend/public/images/help/predefined_layouts.png
 create mode 100644 frontend/public/images/help/scene1_1.PNG
 create mode 100644 frontend/public/images/help/themes.png
 create mode 100644 frontend/public/images/help/vr.png
 create mode 100644 frontend/src/ui/help.ts

diff --git a/PLAN.md b/PLAN.md
index d03b277..b2e1ccd 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -195,7 +195,7 @@ Render spine done and **verified live** (playwright-cli: config loads from `/api
 - [x] Migrate Edge Actions panel (`views/edge.R` → `src/ui/edge.ts`) — direction toggle (shows arrow-size sliders), opacity-by-weight (hides manual opacity sliders — both show/hide behaviours ported from `functions/edges.R`), intra/inter arrow-size + edge-opacity + channel-curvature sliders, highlight-selected + loaded-edge-color-priority checkboxes, and the channel color/Hide list (`attachChannelEditList`, rebuilt on `network:loaded`). Verified live (defaults hide opacity+arrow sliders, toggles reveal them, curvature 18/8, channel color→#123456, channel Hide→visibility false).
 - [x] Migrate View Data panel (`views/data.R` → `src/ui/data.ts`) — 6 sub-tabs (Network Data, Selected Edges, Clustering Data, Degree, Clustering Coefficient, Betweenness Centrality) as plain HTML tables with CSV download, replacing v2 DT datatables. Network table fills on `network:loaded`; Clustering/metric tables on `clustering:applied`/`topology:applied` (added `metric` to the topology event payload + `ApplyTopologyCommand` so results route to the right tab); Selected Edges recomputed from `ctx` on panel open (port of `functions/edges.R updateSelectedEdgesView`). Metric tabs hidden until their event fires (v2 `hideDataMetricTabs`), Selected Edges re-hides when nothing selected. Verified live (Network Data 21 aspirin rows, Degree tab appears after run w/ layers selected, Selected Edges 3 channel-rows on edge select then hides on deselect).
 - [x] Migrate FPS panel (`views/fps.R` → `src/ui/fps.ts`) — 15/30/60 radios set `ctx.fps` (read by the `animate` loop). Default checked reflects the real `ctx.fps` (30) rather than v2's UI default of 15. Verified live (30 checked, switches to 60/15).
-- [ ] Migrate Help panel (`views/help.R` → `src/ui/help.ts`) — 920 lines of mostly static HTML; port as an HTML fragment, not TS
+- [x] Migrate Help panel (`views/help.R` → `src/ui/help.ts`) — static HTML fragment in `#panel-help` with 12 inner tabs (data-attr driven `openHelpTab`, Examples default). Help images + example data copied to `public/images/help/` + `public/data/`; `./`→`/` asset paths, `max-width:100%` on imgs, `pre` scrolls. Help CSS ported into `style.css` (`#helpDiv .tab/.tabcontent/.numbering/.indent/.last_p`). Verified live (12 tabs, tab-switch Examples↔API, 19/19 images load).
 - [ ] Migrate footer (`views/footer.R` → static footer in `index.html`)
 - [x] Port edge-panel toggles from `functions/edges.R` into `src/ui/edge.ts` (done with the Edge panel above: `handleEdgeWidthByWeightCheckbox`/`handleEdgeDirectionCheckbox` slider show/hide). `updateSelectedEdgesView` ported into `src/ui/data.ts` (recomputed from `ctx` on View Data panel open). `functions/edges.R` fully ported; delete in Phase 14 cleanup.
 - [ ] Add Undo/Redo buttons wired to `CommandHistory`
diff --git a/frontend/public/data/Arena3DwebApp_aspirin.json b/frontend/public/data/Arena3DwebApp_aspirin.json
new file mode 100644
index 0000000..5dc1da8
--- /dev/null
+++ b/frontend/public/data/Arena3DwebApp_aspirin.json
@@ -0,0 +1,751 @@
+{
+    "scene": {
+        "position_x": "85",
+        "position_y": "-19",
+        "scale": "0.809455827869314",
+        "color": "#ffffff",
+        "rotation_x": "-2.78896419700599",
+        "rotation_y": "-0.302066641937101",
+        "rotation_z": "0.0029771022900188"
+    },
+    "layers": [
+        {
+            "name": "enriched_term",
+            "position_x": "442.058002797784",
+            "position_y": "160.901161622552",
+            "position_z": "233.674720828911",
+            "last_layer_scale": "1",
+            "rotation_x": "0",
+            "rotation_y": "0",
+            "rotation_z": "0",
+            "floor_current_color": "#ddb3ff",
+            "geometry_parameters_width": "603.326074473483"
+        },
+        {
+            "name": "protein",
+            "position_x": "-81.1191257882335",
+            "position_y": "-78.6699211047243",
+            "position_z": "-125.599774259377",
+            "last_layer_scale": "1",
+            "rotation_x": "0",
+            "rotation_y": "0",
+            "rotation_z": "0",
+            "floor_current_color": "#ddb3ff",
+            "geometry_parameters_width": "603.326074473483"
+        },
+        {
+            "name": "compound",
+            "position_x": "-410.500521507677",
+            "position_y": "120.553179984998",
+            "position_z": "326.354756898516",
+            "last_layer_scale": "1",
+            "rotation_x": "0",
+            "rotation_y": "0",
+            "rotation_z": "0",
+            "floor_current_color": "#ddb3ff",
+            "geometry_parameters_width": "603.326074473483"
+        }
+    ],
+    "nodes": [
+        {
+            "name": "Small cell lung cancer",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "-184.390180387424",
+            "position_z": "-0.235312365146985",
+            "scale": "0.5",
+            "color": "#D9D9D9",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Regulation of lipolysis in adipocytes",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "-208.898649698721",
+            "position_z": "96.0095153453069",
+            "scale": "0.5",
+            "color": "#D9D9D9",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Serotonergic synapse",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "-248.554186056634",
+            "position_z": "59.2472624012911",
+            "scale": "0.5",
+            "color": "#D9D9D9",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Platelet activation",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "-248.554186056634",
+            "position_z": "-59.7178871315854",
+            "scale": "0.5",
+            "color": "#D9D9D9",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Arachidonic acid metabolism",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "-208.898649698721",
+            "position_z": "-96.4801400756012",
+            "scale": "0.5",
+            "color": "#D9D9D9",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Thrombosis",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "255.264259857242",
+            "position_z": "-5.5065133261044",
+            "scale": "0.5",
+            "color": "#3690C0",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Blood platelet",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "60.0846252433915",
+            "position_z": "-1.45083035896154",
+            "scale": "0.653846153846154",
+            "color": "#99D8C9",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Connective tissue",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "25.6560387280491",
+            "position_z": "54.1468041959962",
+            "scale": "0.807692307692308",
+            "color": "#99D8C9",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Peripheral artery disease",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "279.629653012579",
+            "position_z": "-84.5585799875458",
+            "scale": "0.5",
+            "color": "#3690C0",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Blood vessel",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "-23.1214547872933",
+            "position_z": "-1.45083035896154",
+            "scale": "0.807692307692308",
+            "color": "#99D8C9",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "Blood",
+            "layer": "enriched_term",
+            "position_x": "-10",
+            "position_y": "1.74119372804912",
+            "position_z": "-80.9633099139193",
+            "scale": "1.11538461538462",
+            "color": "#99D8C9",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TP53",
+            "layer": "protein",
+            "position_x": "-10",
+            "position_y": "-243.463965867211",
+            "position_z": "37.4827708709913",
+            "scale": "1.42307692307692",
+            "color": "#FF0000",
+            "url": "",
+            "descr": "Cellular tumor antigen p53; Acts as a tumor suppressor in many tumor types; induces growth arrest or apoptosis depending on the physiological circumstances and cell type. Involved in cell cycle regulation as a trans-activator that acts to negatively regulate cell division by controlling a set of genes required for this process. One of the activated genes is an inhibitor of cyclin-dependent kinases. Apoptosis induction seems to be mediated either by stimulation of BAX and FAS antigen expression or by repression of Bcl-2 expression. In cooperation with mitochondrial PPIF is involved in activating oxidative stress-induced necrosis; the function is largely independent of transcription. Induces the transcription of long intergenic non-coding RNA p21 (lincRNA-p21) and lincRNA- Mkln1. LincRNA-p21 participates in TP53-dependent transcriptional repression leading to apoptosis and seem to have to effect on cell-cycle regulation. Implicated in Notch signaling cross-over. Prevents CDK7 kinase activity when associated to CAK complex in response to DNA damage thus stopping cell cycle progression. Isoform 2 enhances the transactivation activity of isoform 1 from some but not all TP53-inducible promoters. Isoform 4 suppresses transactivation activity and impairs growth suppression mediated by isoform 1. Isoform 7 inhibits isoform 1-mediated apoptosis. Regulates the circadian clock by repressing CLOCK-ARNTL/BMAL1- mediated transcriptional activation of PER2."
+        },
+        {
+            "name": "warfarin",
+            "layer": "compound",
+            "position_x": "-10",
+            "position_y": "-183.309324858094",
+            "position_z": "-133.489189479766",
+            "scale": "0.961538461538462",
+            "color": "#FFC580",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "ibuprofen",
+            "layer": "compound",
+            "position_x": "-10",
+            "position_y": "55.5495507646588",
+            "position_z": "254.759524441928",
+            "scale": "1.11538461538462",
+            "color": "#E8FF00",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "clopidogrel",
+            "layer": "compound",
+            "position_x": "-10",
+            "position_y": "219.148397777884",
+            "position_z": "-254.453267529946",
+            "scale": "0.961538461538462",
+            "color": "#AEFF80",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "SELP",
+            "layer": "protein",
+            "position_x": "-10",
+            "position_y": "162.757038084004",
+            "position_z": "-124.383602137524",
+            "scale": "1.88461538461538",
+            "color": "#00FF2E",
+            "url": "",
+            "descr": "Platelet activation dependent granule-external membrane protein; Ca(2+)-dependent receptor for myeloid cells that binds to carbohydrates on neutrophils and monocytes. Mediates the interaction of activated endothelial cells or platelets with leukocytes. The ligand recognized is sialyl-Lewis X. Mediates rapid rolling of leukocyte rolling over vascular surfaces during the initial steps in inflammation through interaction with PSGL1; Belongs to the selectin/LECAM family."
+        },
+        {
+            "name": "TBXA2R",
+            "layer": "protein",
+            "position_x": "-10",
+            "position_y": "269.733593919743",
+            "position_z": "61.7684463326417",
+            "scale": "1.57692307692308",
+            "color": "#80FFDC",
+            "url": "",
+            "descr": "Thromboxane A2 receptor; Receptor for thromboxane A2 (TXA2) a potent stimulator of platelet aggregation. The activity of this receptor is mediated by a G-protein that activates a phosphatidylinositol-calcium second messenger system. In the kidney the binding of TXA2 to glomerular TP receptors causes intense vasoconstriction. Activates phospholipase C. Isoform 1 activates adenylyl cyclase. Isoform 2 inhibits adenylyl cyclase."
+        },
+        {
+            "name": "CRP",
+            "layer": "protein",
+            "position_x": "-10",
+            "position_y": "60.2105170350341",
+            "position_z": "-150.420318051157",
+            "scale": "1.73076923076923",
+            "color": "#00B9FF",
+            "url": "",
+            "descr": "C-reactive protein pentraxin-related; Displays several functions associated with host defense: it promotes agglutination bacterial capsular swelling phagocytosis and complement fixation through its calcium-dependent binding to phosphorylcholine. Can interact with DNA and histones and may scavenge nuclear material released from damaged circulating cells; Short pentraxins"
+        },
+        {
+            "name": "salicylate",
+            "layer": "compound",
+            "position_x": "-10",
+            "position_y": "190.916466660542",
+            "position_z": "121.062673551418",
+            "scale": "1.26923076923077",
+            "color": "#8097FF",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "PTGS1",
+            "layer": "protein",
+            "position_x": "-10",
+            "position_y": "88.2359754509011",
+            "position_z": "144.912065235429",
+            "scale": "2.19230769230769",
+            "color": "#5D00FF",
+            "url": "",
+            "descr": "Prostaglandin-endoperoxide synthase 1 (prostaglandin G/H synthase and cyclooxygenase); Converts arachidonate to prostaglandin H2 (PGH2) a committed step in prostanoid synthesis. Involved in the constitutive production of prostanoids in particular in the stomach and platelets. In gastric epithelial cells it is a key step in the generation of prostaglandins such as prostaglandin E2 (PGE2) which plays an important role in cytoprotection. In platelets it is involved in the generation of thromboxane A2 (TXA2) which promotes platelet activation and aggregation vasoconstriction and proliferation of vascular smooth muscle cells; Belongs to the prostaglandin G/H synthase family."
+        },
+        {
+            "name": "PTGS2",
+            "layer": "protein",
+            "position_x": "-10",
+            "position_y": "-35.1740864612164",
+            "position_z": "122.803637014825",
+            "scale": "2.5",
+            "color": "#F380FF",
+            "url": "",
+            "descr": "Prostaglandin-endoperoxide synthase 2 (prostaglandin G/H synthase and cyclooxygenase); Converts arachidonate to prostaglandin H2 (PGH2) a committed step in prostanoid synthesis. Constitutively expressed in some tissues in physiological conditions such as the endothelium kidney and brain and in pathological conditions such as in cancer. PTGS2 is responsible for production of inflammatory prostaglandins. Up-regulation of PTGS2 is also associated with increased cell adhesion phenotypic changes resistance to apoptosis and tumor angiogenesis. In cancer cells PTGS2 is a key step in the production of prostaglandin E2 (PGE2) which plays important roles in modulating motility proliferation and resistance to apoptosis; Belongs to the prostaglandin G/H synthase family."
+        },
+        {
+            "name": "aspirin",
+            "layer": "compound",
+            "position_x": "-10",
+            "position_y": "247.498437602146",
+            "position_z": "9.03979852559709",
+            "scale": "1.73076923076923",
+            "color": "#FF008B",
+            "url": "",
+            "descr": ""
+        }
+    ],
+    "edges": [
+        {
+            "src": "PTGS2_protein",
+            "trg": "Small cell lung cancer_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Small cell lung cancer_enriched_term",
+            "trg": "TP53_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "PTGS2_protein",
+            "trg": "Regulation of lipolysis in adipocytes_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "PTGS1_protein",
+            "trg": "Regulation of lipolysis in adipocytes_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "PTGS2_protein",
+            "trg": "Serotonergic synapse_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "PTGS1_protein",
+            "trg": "Serotonergic synapse_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Platelet activation_enriched_term",
+            "trg": "TBXA2R_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "PTGS1_protein",
+            "trg": "Platelet activation_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Arachidonic acid metabolism_enriched_term",
+            "trg": "PTGS2_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Arachidonic acid metabolism_enriched_term",
+            "trg": "PTGS1_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "SELP_protein",
+            "trg": "Thrombosis_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "CRP_protein",
+            "trg": "Thrombosis_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood platelet_enriched_term",
+            "trg": "TBXA2R_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood platelet_enriched_term",
+            "trg": "PTGS1_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood platelet_enriched_term",
+            "trg": "SELP_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Connective tissue_enriched_term",
+            "trg": "PTGS2_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Connective tissue_enriched_term",
+            "trg": "PTGS1_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Connective tissue_enriched_term",
+            "trg": "TP53_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "CRP_protein",
+            "trg": "Connective tissue_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Peripheral artery disease_enriched_term",
+            "trg": "SELP_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "CRP_protein",
+            "trg": "Peripheral artery disease_enriched_term",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood vessel_enriched_term",
+            "trg": "TBXA2R_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood vessel_enriched_term",
+            "trg": "PTGS2_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood vessel_enriched_term",
+            "trg": "SELP_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood vessel_enriched_term",
+            "trg": "CRP_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood_enriched_term",
+            "trg": "TBXA2R_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood_enriched_term",
+            "trg": "PTGS2_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood_enriched_term",
+            "trg": "PTGS1_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood_enriched_term",
+            "trg": "TP53_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood_enriched_term",
+            "trg": "SELP_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "Blood_enriched_term",
+            "trg": "CRP_protein",
+            "opacity": "0.500500500500501",
+            "color": "#8DD3C7",
+            "channel": "enrichment"
+        },
+        {
+            "src": "TP53_protein",
+            "trg": "salicylate_compound",
+            "opacity": "0.451745863510569",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "TP53_protein",
+            "trg": "aspirin_compound",
+            "opacity": "0.628275334157687",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "TP53_protein",
+            "trg": "warfarin_compound",
+            "opacity": "0.326993660326994",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "PTGS2_protein",
+            "trg": "TP53_protein",
+            "opacity": "0.659428055506487",
+            "color": "#BEBADA",
+            "channel": "pp"
+        },
+        {
+            "src": "ibuprofen_compound",
+            "trg": "warfarin_compound",
+            "opacity": "0.182719974876838",
+            "color": "#FB8072",
+            "channel": "cc"
+        },
+        {
+            "src": "clopidogrel_compound",
+            "trg": "warfarin_compound",
+            "opacity": "0.560325030913266",
+            "color": "#FB8072",
+            "channel": "cc"
+        },
+        {
+            "src": "SELP_protein",
+            "trg": "salicylate_compound",
+            "opacity": "0.427093760427094",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "SELP_protein",
+            "trg": "aspirin_compound",
+            "opacity": "0.652888182299947",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "SELP_protein",
+            "trg": "clopidogrel_compound",
+            "opacity": "0.599772321340949",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "PTGS2_protein",
+            "trg": "SELP_protein",
+            "opacity": "0.12012012012012",
+            "color": "#BEBADA",
+            "channel": "pp"
+        },
+        {
+            "src": "PTGS1_protein",
+            "trg": "SELP_protein",
+            "opacity": "0.144203026555968",
+            "color": "#BEBADA",
+            "channel": "pp"
+        },
+        {
+            "src": "TBXA2R_protein",
+            "trg": "aspirin_compound",
+            "opacity": "0.652888182299947",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "TBXA2R_protein",
+            "trg": "clopidogrel_compound",
+            "opacity": "0.154829339143065",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "TBXA2R_protein",
+            "trg": "ibuprofen_compound",
+            "opacity": "0.183418712830478",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "CRP_protein",
+            "trg": "aspirin_compound",
+            "opacity": "0.653547665312371",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "CRP_protein",
+            "trg": "clopidogrel_compound",
+            "opacity": "0.65025025025025",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "CRP_protein",
+            "trg": "TP53_protein",
+            "opacity": "0.117058234705294",
+            "color": "#BEBADA",
+            "channel": "pp"
+        },
+        {
+            "src": "CRP_protein",
+            "trg": "SELP_protein",
+            "opacity": "0.45229543268759",
+            "color": "#BEBADA",
+            "channel": "pp"
+        },
+        {
+            "src": "CRP_protein",
+            "trg": "PTGS2_protein",
+            "opacity": "0.269634340222576",
+            "color": "#BEBADA",
+            "channel": "pp"
+        },
+        {
+            "src": "salicylate_compound",
+            "trg": "warfarin_compound",
+            "opacity": "0.187246069599011",
+            "color": "#FB8072",
+            "channel": "cc"
+        },
+        {
+            "src": "ibuprofen_compound",
+            "trg": "salicylate_compound",
+            "opacity": "0.45229543268759",
+            "color": "#FB8072",
+            "channel": "cc"
+        },
+        {
+            "src": "aspirin_compound",
+            "trg": "salicylate_compound",
+            "opacity": "0.660754872519578",
+            "color": "#FB8072",
+            "channel": "cc"
+        },
+        {
+            "src": "PTGS1_protein",
+            "trg": "salicylate_compound",
+            "opacity": "0.65025025025025",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "PTGS1_protein",
+            "trg": "aspirin_compound",
+            "opacity": "0.666666666666667",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "PTGS1_protein",
+            "trg": "ibuprofen_compound",
+            "opacity": "0.665999332665999",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "PTGS1_protein",
+            "trg": "PTGS2_protein",
+            "opacity": "0.504865649963689",
+            "color": "#BEBADA",
+            "channel": "pp"
+        },
+        {
+            "src": "PTGS1_protein",
+            "trg": "TBXA2R_protein",
+            "opacity": "0.167500834167501",
+            "color": "#BEBADA",
+            "channel": "pp"
+        },
+        {
+            "src": "PTGS2_protein",
+            "trg": "salicylate_compound",
+            "opacity": "0.650909733262674",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "PTGS2_protein",
+            "trg": "aspirin_compound",
+            "opacity": "0.666666666666667",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "PTGS2_protein",
+            "trg": "ibuprofen_compound",
+            "opacity": "0.658101238493395",
+            "color": "#FFFFB3",
+            "channel": "pc"
+        },
+        {
+            "src": "PTGS2_protein",
+            "trg": "TBXA2R_protein",
+            "opacity": "0.120402755696873",
+            "color": "#BEBADA",
+            "channel": "pp"
+        },
+        {
+            "src": "aspirin_compound",
+            "trg": "warfarin_compound",
+            "opacity": "0.628923040687747",
+            "color": "#FB8072",
+            "channel": "cc"
+        },
+        {
+            "src": "aspirin_compound",
+            "trg": "ibuprofen_compound",
+            "opacity": "0.635341223576518",
+            "color": "#FB8072",
+            "channel": "cc"
+        },
+        {
+            "src": "aspirin_compound",
+            "trg": "clopidogrel_compound",
+            "opacity": "0.637947751673242",
+            "color": "#FB8072",
+            "channel": "cc"
+        }
+    ],
+    "universalLabelColor": "#000000"
+}
\ No newline at end of file
diff --git a/frontend/public/data/GPCRs_data.tsv b/frontend/public/data/GPCRs_data.tsv
new file mode 100644
index 0000000..13b9297
--- /dev/null
+++ b/frontend/public/data/GPCRs_data.tsv
@@ -0,0 +1,333 @@
+SourceNode	SourceLayer	TargetNode	TargetLayer
+EDNRB	GPCRs	EDNRA	GPCRs
+EDNRB	GPCRs	GRK2	GRKs
+EDNRB	GPCRs	GRK6	GRKs
+EDNRA	GPCRs	GRK6	GRKs
+EDNRB	GPCRs	GNA14	G-proteins
+EDNRB	GPCRs	GNAI2	G-proteins
+EDNRB	GPCRs	GNAI3	G-proteins
+EDNRB	GPCRs	GNA11	G-proteins
+EDNRB	GPCRs	GNAQ	G-proteins
+EDNRB	GPCRs	GNAI1	G-proteins
+EDNRB	GPCRs	GNA13	G-proteins
+EDNRA	GPCRs	GNAI2	G-proteins
+EDNRA	GPCRs	GNAI3	G-proteins
+EDNRA	GPCRs	GNA11	G-proteins
+EDNRA	GPCRs	GNAI1	G-proteins
+EDNRB	GPCRs	EDN1	Effectors
+EDNRA	GPCRs	EDN1	Effectors
+GRK2	GRKs	GRK6	GRKs
+GNAI2	G-proteins	ADCY6	Effectors
+GNAI2	G-proteins	GPRIN2	Effectors
+GNAI2	G-proteins	TUBB4A	Effectors
+GNAI2	G-proteins	NPM1	Effectors
+GNAI2	G-proteins	TUBB	Effectors
+GNAI2	G-proteins	HSP90AB1	Effectors
+GNAI2	G-proteins	RAC2	Effectors
+GNAI2	G-proteins	TUBG1	Effectors
+GNAI2	G-proteins	MYH9	Effectors
+GNAI2	G-proteins	CAPZA2	Effectors
+GNAI2	G-proteins	PTEN	Effectors
+GNAI2	G-proteins	RAC3	Effectors
+GNAI2	G-proteins	RAC1	Effectors
+GNAI2	G-proteins	TUBA1B	Effectors
+GNAI2	G-proteins	TUBA4A	Effectors
+GNAI2	G-proteins	TUBB4B	Effectors
+GNAI2	G-proteins	TUBB3	Effectors
+GNAI2	G-proteins	TUBB2A	Effectors
+GNAI2	G-proteins	RAP1GAP2	Effectors
+GNAI2	G-proteins	TUBA1A	Effectors
+GNAI2	G-proteins	TUBGCP3	Effectors
+GNAI2	G-proteins	ITCH	Effectors
+GNAI2	G-proteins	TUBGCP6	Effectors
+GNAI2	G-proteins	TUBGCP5	Effectors
+GNAI2	G-proteins	TTC1	Effectors
+GNAI2	G-proteins	TUBA1C	Effectors
+GNAI2	G-proteins	TUBGCP2	Effectors
+GNAI2	G-proteins	TUBB6	Effectors
+GNAI2	G-proteins	TUBB2B	Effectors
+GNAI2	G-proteins	TUBB1	Effectors
+GNAI2	G-proteins	RIC8A	Effectors
+GNAI2	G-proteins	TUBG2	Effectors
+GNAI2	G-proteins	TUBA8	Effectors
+GNAI2	G-proteins	TUBE1	Effectors
+GNAI2	G-proteins	GNAI3	G-proteins
+GNAI2	G-proteins	GNAI1	G-proteins
+GNAI3	G-proteins	ADCY6	Effectors
+GNAI3	G-proteins	GPRIN2	Effectors
+GNAI3	G-proteins	TUBB4A	Effectors
+GNAI3	G-proteins	TUBB	Effectors
+GNAI3	G-proteins	RAC2	Effectors
+GNAI3	G-proteins	TUBG1	Effectors
+GNAI3	G-proteins	YWHAQ	Effectors
+GNAI3	G-proteins	RANGAP1	Effectors
+GNAI3	G-proteins	RAC3	Effectors
+GNAI3	G-proteins	RAC1	Effectors
+GNAI3	G-proteins	TUBA1B	Effectors
+GNAI3	G-proteins	TUBA4A	Effectors
+GNAI3	G-proteins	TUBB4B	Effectors
+GNAI3	G-proteins	YWHAH	Effectors
+GNAI3	G-proteins	TUBB3	Effectors
+GNAI3	G-proteins	TUBB2A	Effectors
+GNAI3	G-proteins	RAP1GAP2	Effectors
+GNAI3	G-proteins	TUBA1A	Effectors
+GNAI3	G-proteins	USP33	Effectors
+GNAI3	G-proteins	TUBGCP3	Effectors
+GNAI3	G-proteins	TUBGCP6	Effectors
+GNAI3	G-proteins	TUBGCP5	Effectors
+GNAI3	G-proteins	TUBA1C	Effectors
+GNAI3	G-proteins	TUBGCP2	Effectors
+GNAI3	G-proteins	TUBB6	Effectors
+GNAI3	G-proteins	TUBB2B	Effectors
+GNAI3	G-proteins	TUBB1	Effectors
+GNAI3	G-proteins	RIC8A	Effectors
+GNAI3	G-proteins	TUBG2	Effectors
+GNAI3	G-proteins	TUBA8	Effectors
+GNAI3	G-proteins	TUBE1	Effectors
+GNAI3	G-proteins	TRAF6	Effectors
+GNAI3	G-proteins	GNAI1	G-proteins
+GNAI1	G-proteins	ADCY6	Effectors
+GNAI1	G-proteins	PDE6D	Effectors
+GNAI1	G-proteins	GPRIN2	Effectors
+GNAI1	G-proteins	TUBB4A	Effectors
+GNAI1	G-proteins	JUN	Effectors
+GNAI1	G-proteins	TUBB	Effectors
+GNAI1	G-proteins	RAC2	Effectors
+GNAI1	G-proteins	TUBG1	Effectors
+GNAI1	G-proteins	MYH9	Effectors
+GNAI1	G-proteins	RANGAP1	Effectors
+GNAI1	G-proteins	PTEN	Effectors
+GNAI1	G-proteins	RAC3	Effectors
+GNAI1	G-proteins	RAC1	Effectors
+GNAI1	G-proteins	TUBA1B	Effectors
+GNAI1	G-proteins	TUBA4A	Effectors
+GNAI1	G-proteins	TUBB4B	Effectors
+GNAI1	G-proteins	TUBB3	Effectors
+GNAI1	G-proteins	TUBB2A	Effectors
+GNAI1	G-proteins	RAP1GAP2	Effectors
+GNAI1	G-proteins	TUBA1A	Effectors
+GNAI1	G-proteins	TUBGCP3	Effectors
+GNAI1	G-proteins	TUBGCP6	Effectors
+GNAI1	G-proteins	TUBGCP5	Effectors
+GNAI1	G-proteins	TUBA1C	Effectors
+GNAI1	G-proteins	TUBGCP2	Effectors
+GNAI1	G-proteins	TUBB6	Effectors
+GNAI1	G-proteins	TUBB2B	Effectors
+GNAI1	G-proteins	TUBB1	Effectors
+GNAI1	G-proteins	RIC8A	Effectors
+GNAI1	G-proteins	TUBG2	Effectors
+GNAI1	G-proteins	TUBA8	Effectors
+GNAI1	G-proteins	TUBE1	Effectors
+GNAI1	G-proteins	GNA13	G-proteins
+GNA14	G-proteins	GRK2	GRKs
+GNA14	G-proteins	PLCB2	Effectors
+GNA11	G-proteins	GRK2	GRKs
+GNA11	G-proteins	PLCB2	Effectors
+GNAQ	G-proteins	ARHGEF11	Effectors
+GNAQ	G-proteins	TUBB4A	Effectors
+GNAQ	G-proteins	TUBB	Effectors
+GNAQ	G-proteins	TUBG1	Effectors
+GNAQ	G-proteins	GRK2	GRKs
+GNAQ	G-proteins	PIK3CA	Effectors
+GNAQ	G-proteins	PIK3CB	Effectors
+GNAQ	G-proteins	ARF6	Effectors
+GNAQ	G-proteins	TUBA1B	Effectors
+GNAQ	G-proteins	TUBA4A	Effectors
+GNAQ	G-proteins	TUBB4B	Effectors
+GNAQ	G-proteins	PLCB2	Effectors
+GNAQ	G-proteins	AKAP13	Effectors
+GNAQ	G-proteins	TUBB3	Effectors
+GNAQ	G-proteins	TUBB2A	Effectors
+GNAQ	G-proteins	TUBA1A	Effectors
+GNAQ	G-proteins	ARHGEF1	Effectors
+GNAQ	G-proteins	TUBGCP3	Effectors
+GNAQ	G-proteins	TUBGCP6	Effectors
+GNAQ	G-proteins	TUBGCP5	Effectors
+GNAQ	G-proteins	TTC1	Effectors
+GNAQ	G-proteins	TUBA1C	Effectors
+GNAQ	G-proteins	TUBGCP2	Effectors
+GNAQ	G-proteins	TUBB6	Effectors
+GNAQ	G-proteins	TUBB2B	Effectors
+GNAQ	G-proteins	TUBB1	Effectors
+GNAQ	G-proteins	RIC8A	Effectors
+GNAQ	G-proteins	TUBG2	Effectors
+GNAQ	G-proteins	TUBA8	Effectors
+GNAQ	G-proteins	ARHGEF12	Effectors
+GNAQ	G-proteins	TUBE1	Effectors
+GNA13	G-proteins	ARHGEF11	Effectors
+GNA13	G-proteins	CDH1	Effectors
+GNA13	G-proteins	ARHGEF1	Effectors
+GNA13	G-proteins	MAP3K5	Effectors
+GNA13	G-proteins	RIC8A	Effectors
+GNA13	G-proteins	ARHGEF12	Effectors
+JUN	Effectors	NPM1	Effectors
+JUN	Effectors	TUBB	Effectors
+JUN	Effectors	HSP90AB1	Effectors
+JUN	Effectors	YWHAQ	Effectors
+JUN	Effectors	MYH9	Effectors
+JUN	Effectors	RANGAP1	Effectors
+JUN	Effectors	RAC1	Effectors
+JUN	Effectors	TUBA1B	Effectors
+JUN	Effectors	TUBA4A	Effectors
+JUN	Effectors	TUBB4B	Effectors
+JUN	Effectors	YWHAH	Effectors
+JUN	Effectors	TUBB3	Effectors
+JUN	Effectors	TUBB2A	Effectors
+JUN	Effectors	TUBA1A	Effectors
+JUN	Effectors	ITCH	Effectors
+JUN	Effectors	TUBGCP2	Effectors
+JUN	Effectors	TUBB6	Effectors
+JUN	Effectors	TUBB2B	Effectors
+JUN	Effectors	TUBB1	Effectors
+MYH9	Effectors	CAPZA2	Effectors
+MYH9	Effectors	RAC1	Effectors
+MYH9	Effectors	TUBA4A	Effectors
+CAPZA2	Effectors	TUBB6	Effectors
+PTEN	Effectors	TUBA1B	Effectors
+PTEN	Effectors	TUBB4B	Effectors
+PTEN	Effectors	TUBB3	Effectors
+PTEN	Effectors	TUBB2A	Effectors
+PTEN	Effectors	TUBA1C	Effectors
+TUBB3	Effectors	TUBB2A	Effectors
+TUBB3	Effectors	TUBA1A	Effectors
+TUBB3	Effectors	TUBA1C	Effectors
+TUBB3	Effectors	TUBB2B	Effectors
+TUBB3	Effectors	TUBB1	Effectors
+TUBA1A	Effectors	TUBGCP3	Effectors
+TUBA1A	Effectors	TTC1	Effectors
+TUBA1A	Effectors	TUBA1C	Effectors
+TUBA1A	Effectors	TUBGCP2	Effectors
+TUBA1A	Effectors	TUBB6	Effectors
+TUBA1A	Effectors	TUBB2B	Effectors
+ARF6	Effectors	RAC1	Effectors
+ARF6	Effectors	TUBA1A	Effectors
+CDH1	Effectors	MYH9	Effectors
+CDH1	Effectors	RANGAP1	Effectors
+CDH1	Effectors	PTEN	Effectors
+CDH1	Effectors	TUBB4B	Effectors
+CDH1	Effectors	ARHGEF12	Effectors
+TUBB	Effectors	HSP90AB1	Effectors
+TUBB	Effectors	CDH1	Effectors
+TUBB	Effectors	TUBG1	Effectors
+TUBB	Effectors	GRK2	GRKs
+TUBB	Effectors	YWHAQ	Effectors
+TUBB	Effectors	PTEN	Effectors
+TUBB	Effectors	TUBA1B	Effectors
+TUBB	Effectors	TUBA4A	Effectors
+TUBB	Effectors	TUBB4B	Effectors
+TUBB	Effectors	TUBB3	Effectors
+TUBB	Effectors	TUBB2A	Effectors
+TUBB	Effectors	TUBA1A	Effectors
+TUBB	Effectors	TUBGCP3	Effectors
+TUBB	Effectors	TUBA1C	Effectors
+TUBB	Effectors	TUBGCP2	Effectors
+TUBB	Effectors	TRAF6	Effectors
+YWHAQ	Effectors	MYH9	Effectors
+YWHAQ	Effectors	PIK3CB	Effectors
+YWHAQ	Effectors	TUBA1B	Effectors
+YWHAQ	Effectors	YWHAH	Effectors
+YWHAQ	Effectors	TUBB3	Effectors
+YWHAQ	Effectors	TUBB2A	Effectors
+YWHAQ	Effectors	TUBA1A	Effectors
+YWHAQ	Effectors	TUBGCP3	Effectors
+YWHAQ	Effectors	MAP3K5	Effectors
+YWHAQ	Effectors	TUBA1C	Effectors
+PIK3CA	Effectors	RAC1	Effectors
+PIK3CA	Effectors	YWHAH	Effectors
+PIK3CA	Effectors	ARHGEF1	Effectors
+TUBA1C	Effectors	TUBB6	Effectors
+TUBA1C	Effectors	TUBB2B	Effectors
+TUBB6	Effectors	TRAF6	Effectors
+NPM1	Effectors	HSP90AB1	Effectors
+NPM1	Effectors	YWHAQ	Effectors
+NPM1	Effectors	TUBB3	Effectors
+NPM1	Effectors	TUBA1A	Effectors
+NPM1	Effectors	TUBA1C	Effectors
+NPM1	Effectors	TRAF6	Effectors
+RAC1	Effectors	TUBA4A	Effectors
+RAC1	Effectors	PLCB2	Effectors
+RAC1	Effectors	AKAP13	Effectors
+RAC1	Effectors	TRAF6	Effectors
+ITCH	Effectors	TRAF6	Effectors
+HSP90AB1	Effectors	CDH1	Effectors
+HSP90AB1	Effectors	TUBG1	Effectors
+HSP90AB1	Effectors	GRK2	GRKs
+HSP90AB1	Effectors	MYH9	Effectors
+HSP90AB1	Effectors	GRK6	GRKs
+HSP90AB1	Effectors	PTEN	Effectors
+HSP90AB1	Effectors	TUBB3	Effectors
+HSP90AB1	Effectors	TUBA1A	Effectors
+HSP90AB1	Effectors	TUBGCP3	Effectors
+HSP90AB1	Effectors	MAP3K5	Effectors
+HSP90AB1	Effectors	TUBA1C	Effectors
+HSP90AB1	Effectors	TUBGCP2	Effectors
+MAP3K5	Effectors	TRAF6	Effectors
+TUBB4A	Effectors	JUN	Effectors
+TUBB4A	Effectors	TUBB	Effectors
+TUBB4A	Effectors	TUBA4A	Effectors
+TUBB4A	Effectors	TUBA1C	Effectors
+TUBB4A	Effectors	TUBGCP2	Effectors
+TUBB4A	Effectors	TRAF6	Effectors
+TUBB2B	Effectors	TUBB1	Effectors
+TUBB2B	Effectors	TRAF6	Effectors
+TUBG1	Effectors	TUBA1B	Effectors
+TUBG1	Effectors	TUBA4A	Effectors
+TUBG1	Effectors	TUBB4B	Effectors
+TUBG1	Effectors	YWHAH	Effectors
+TUBG1	Effectors	TUBB3	Effectors
+TUBG1	Effectors	TUBB2A	Effectors
+TUBG1	Effectors	TUBA1A	Effectors
+TUBG1	Effectors	TUBGCP3	Effectors
+TUBG1	Effectors	TUBGCP6	Effectors
+TUBG1	Effectors	TUBGCP5	Effectors
+TUBG1	Effectors	TUBA1C	Effectors
+TUBG1	Effectors	TUBGCP2	Effectors
+TUBG1	Effectors	TUBB1	Effectors
+TUBG1	Effectors	TUBG2	Effectors
+TUBB2A	Effectors	TUBA1A	Effectors
+TUBB2A	Effectors	TUBA1C	Effectors
+TUBB2A	Effectors	TUBGCP2	Effectors
+TUBB2A	Effectors	RIC8A	Effectors
+TUBA1B	Effectors	TUBA4A	Effectors
+TUBA1B	Effectors	TUBB4B	Effectors
+TUBA1B	Effectors	TUBB3	Effectors
+TUBA1B	Effectors	TUBB2A	Effectors
+TUBA1B	Effectors	TUBA1A	Effectors
+TUBA1B	Effectors	TUBA1C	Effectors
+TUBA1B	Effectors	TUBGCP2	Effectors
+TUBA1B	Effectors	TUBB6	Effectors
+TUBA1B	Effectors	TUBB2B	Effectors
+TUBA1B	Effectors	TUBB1	Effectors
+YWHAH	Effectors	TUBB3	Effectors
+YWHAH	Effectors	TUBA1A	Effectors
+YWHAH	Effectors	MAP3K5	Effectors
+TUBA4A	Effectors	TUBB4B	Effectors
+TUBA4A	Effectors	TUBB3	Effectors
+TUBA4A	Effectors	TUBB2A	Effectors
+TUBA4A	Effectors	TUBA1A	Effectors
+TUBA4A	Effectors	TUBA1C	Effectors
+TUBA4A	Effectors	TUBGCP2	Effectors
+TUBA4A	Effectors	TUBB6	Effectors
+TUBA4A	Effectors	TUBB2B	Effectors
+TUBGCP2	Effectors	TUBG2	Effectors
+TUBB4B	Effectors	TUBB3	Effectors
+TUBB4B	Effectors	TUBB2A	Effectors
+TUBB4B	Effectors	TUBA1A	Effectors
+TUBB4B	Effectors	TUBGCP3	Effectors
+TUBB4B	Effectors	TUBA1C	Effectors
+TUBB4B	Effectors	TUBGCP2	Effectors
+TUBB4B	Effectors	TUBB6	Effectors
+RAC2	Effectors	RAC3	Effectors
+RAC2	Effectors	RAC1	Effectors
+RAC3	Effectors	RAC1	Effectors
+TUBGCP3	Effectors	TUBGCP6	Effectors
+TUBGCP3	Effectors	TUBGCP5	Effectors
+TUBGCP3	Effectors	TUBGCP2	Effectors
+TUBGCP3	Effectors	TUBG2	Effectors
+TUBGCP6	Effectors	TUBGCP5	Effectors
+TUBGCP6	Effectors	TUBGCP2	Effectors
+TUBGCP6	Effectors	TUBG2	Effectors
+TUBGCP5	Effectors	TUBGCP2	Effectors
+TUBGCP5	Effectors	TUBG2	Effectors
+RANGAP1	Effectors	TRAF6	Effectors
+USP33	Effectors	TRAF6	Effectors
\ No newline at end of file
diff --git a/frontend/public/data/GPCRs_data_edge_attributes.tsv b/frontend/public/data/GPCRs_data_edge_attributes.tsv
new file mode 100644
index 0000000..d995b04
--- /dev/null
+++ b/frontend/public/data/GPCRs_data_edge_attributes.tsv
@@ -0,0 +1,159 @@
+SourceNode	SourceLayer	TargetNode	TargetLayer	Color
+TUBB	Effectors	GRK2	GRKs	#00a2ff
+GNAI2	G-proteins	ADCY6	Effectors	#62e84a
+GNAI2	G-proteins	GPRIN2	Effectors	#62e84a
+GNAI2	G-proteins	TUBB4A	Effectors	#62e84a
+GNAI2	G-proteins	NPM1	Effectors	#62e84a
+GNAI2	G-proteins	TUBB	Effectors	#62e84a
+GNAI2	G-proteins	HSP90AB1	Effectors	#62e84a
+GNAI2	G-proteins	RAC2	Effectors	#62e84a
+GNAI2	G-proteins	TUBG1	Effectors	#62e84a
+GNAI2	G-proteins	MYH9	Effectors	#62e84a
+GNAI2	G-proteins	CAPZA2	Effectors	#62e84a
+GNAI2	G-proteins	PTEN	Effectors	#62e84a
+GNAI2	G-proteins	RAC3	Effectors	#62e84a
+GNAI2	G-proteins	RAC1	Effectors	#62e84a
+GNAI2	G-proteins	TUBA1B	Effectors	#62e84a
+GNAI2	G-proteins	TUBA4A	Effectors	#62e84a
+GNAI2	G-proteins	TUBB4B	Effectors	#62e84a
+GNAI2	G-proteins	TUBB3	Effectors	#62e84a
+GNAI2	G-proteins	TUBB2A	Effectors	#62e84a
+GNAI2	G-proteins	RAP1GAP2	Effectors	#62e84a
+GNAI2	G-proteins	TUBA1A	Effectors	#62e84a
+GNAI2	G-proteins	TUBGCP3	Effectors	#62e84a
+GNAI2	G-proteins	ITCH	Effectors	#62e84a
+GNAI2	G-proteins	TUBGCP6	Effectors	#62e84a
+GNAI2	G-proteins	TUBGCP5	Effectors	#62e84a
+GNAI2	G-proteins	TTC1	Effectors	#62e84a
+GNAI2	G-proteins	TUBA1C	Effectors	#62e84a
+GNAI2	G-proteins	TUBGCP2	Effectors	#62e84a
+GNAI2	G-proteins	TUBB6	Effectors	#62e84a
+GNAI2	G-proteins	TUBB2B	Effectors	#62e84a
+GNAI2	G-proteins	TUBB1	Effectors	#62e84a
+GNAI2	G-proteins	RIC8A	Effectors	#62e84a
+GNAI2	G-proteins	TUBG2	Effectors	#62e84a
+GNAI2	G-proteins	TUBA8	Effectors	#62e84a
+GNAI2	G-proteins	TUBE1	Effectors	#62e84a
+GNAI3	G-proteins	ADCY6	Effectors	#62e84a
+GNAI3	G-proteins	GPRIN2	Effectors	#62e84a
+GNAI3	G-proteins	TUBB4A	Effectors	#62e84a
+GNAI3	G-proteins	TUBB	Effectors	#62e84a
+GNAI3	G-proteins	RAC2	Effectors	#62e84a
+GNAI3	G-proteins	TUBG1	Effectors	#62e84a
+GNAI3	G-proteins	YWHAQ	Effectors	#62e84a
+GNAI3	G-proteins	RANGAP1	Effectors	#62e84a
+GNAI3	G-proteins	RAC3	Effectors	#62e84a
+GNAI3	G-proteins	RAC1	Effectors	#62e84a
+GNAI3	G-proteins	TUBA1B	Effectors	#62e84a
+GNAI3	G-proteins	TUBA4A	Effectors	#62e84a
+GNAI3	G-proteins	TUBB4B	Effectors	#62e84a
+GNAI3	G-proteins	YWHAH	Effectors	#62e84a
+GNAI3	G-proteins	TUBB3	Effectors	#62e84a
+GNAI3	G-proteins	TUBB2A	Effectors	#62e84a
+GNAI3	G-proteins	RAP1GAP2	Effectors	#62e84a
+GNAI3	G-proteins	TUBA1A	Effectors	#62e84a
+GNAI3	G-proteins	USP33	Effectors	#62e84a
+GNAI3	G-proteins	TUBGCP3	Effectors	#62e84a
+GNAI3	G-proteins	TUBGCP6	Effectors	#62e84a
+GNAI3	G-proteins	TUBGCP5	Effectors	#62e84a
+GNAI3	G-proteins	TUBA1C	Effectors	#62e84a
+GNAI3	G-proteins	TUBGCP2	Effectors	#62e84a
+GNAI3	G-proteins	TUBB6	Effectors	#62e84a
+GNAI3	G-proteins	TUBB2B	Effectors	#62e84a
+GNAI3	G-proteins	TUBB1	Effectors	#62e84a
+GNAI3	G-proteins	RIC8A	Effectors	#62e84a
+GNAI3	G-proteins	TUBG2	Effectors	#62e84a
+GNAI3	G-proteins	TUBA8	Effectors	#62e84a
+GNAI3	G-proteins	TUBE1	Effectors	#62e84a
+GNAI3	G-proteins	TRAF6	Effectors	#62e84a
+GNAI1	G-proteins	ADCY6	Effectors	#62e84a
+GNAI1	G-proteins	PDE6D	Effectors	#62e84a
+GNAI1	G-proteins	GPRIN2	Effectors	#62e84a
+GNAI1	G-proteins	TUBB4A	Effectors	#62e84a
+GNAI1	G-proteins	JUN	Effectors	#62e84a
+GNAI1	G-proteins	TUBB	Effectors	#62e84a
+GNAI1	G-proteins	RAC2	Effectors	#62e84a
+GNAI1	G-proteins	TUBG1	Effectors	#62e84a
+GNAI1	G-proteins	MYH9	Effectors	#62e84a
+GNAI1	G-proteins	RANGAP1	Effectors	#62e84a
+GNAI1	G-proteins	PTEN	Effectors	#62e84a
+GNAI1	G-proteins	RAC3	Effectors	#62e84a
+GNAI1	G-proteins	RAC1	Effectors	#62e84a
+GNAI1	G-proteins	TUBA1B	Effectors	#62e84a
+GNAI1	G-proteins	TUBA4A	Effectors	#62e84a
+GNAI1	G-proteins	TUBB4B	Effectors	#62e84a
+GNAI1	G-proteins	TUBB3	Effectors	#62e84a
+GNAI1	G-proteins	TUBB2A	Effectors	#62e84a
+GNAI1	G-proteins	RAP1GAP2	Effectors	#62e84a
+GNAI1	G-proteins	TUBA1A	Effectors	#62e84a
+GNAI1	G-proteins	TUBGCP3	Effectors	#62e84a
+GNAI1	G-proteins	TUBGCP6	Effectors	#62e84a
+GNAI1	G-proteins	TUBGCP5	Effectors	#62e84a
+GNAI1	G-proteins	TUBA1C	Effectors	#62e84a
+GNAI1	G-proteins	TUBGCP2	Effectors	#62e84a
+GNAI1	G-proteins	TUBB6	Effectors	#62e84a
+GNAI1	G-proteins	TUBB2B	Effectors	#62e84a
+GNAI1	G-proteins	TUBB1	Effectors	#62e84a
+GNAI1	G-proteins	RIC8A	Effectors	#62e84a
+GNAI1	G-proteins	TUBG2	Effectors	#62e84a
+GNAI1	G-proteins	TUBA8	Effectors	#62e84a
+GNAI1	G-proteins	TUBE1	Effectors	#62e84a
+HSP90AB1	Effectors	GRK2	GRKs	#00a2ff
+HSP90AB1	Effectors	GRK6	GRKs	#62e84a
+GNA14	G-proteins	GRK2	GRKs	#00a2ff
+GNA14	G-proteins	PLCB2	Effectors	#00a2ff
+GNA11	G-proteins	GRK2	GRKs	#00a2ff
+GNA11	G-proteins	PLCB2	Effectors	#62e84a
+GNAQ	G-proteins	ARHGEF11	Effectors	#00a2ff
+GNAQ	G-proteins	TUBB4A	Effectors	#00a2ff
+GNAQ	G-proteins	TUBB	Effectors	#00a2ff
+GNAQ	G-proteins	TUBG1	Effectors	#00a2ff
+GNAQ	G-proteins	GRK2	GRKs	#00a2ff
+GNAQ	G-proteins	PIK3CA	Effectors	#00a2ff
+GNAQ	G-proteins	PIK3CB	Effectors	#00a2ff
+GNAQ	G-proteins	ARF6	Effectors	#00a2ff
+GNAQ	G-proteins	TUBA1B	Effectors	#00a2ff
+GNAQ	G-proteins	TUBA4A	Effectors	#00a2ff
+GNAQ	G-proteins	TUBB4B	Effectors	#00a2ff
+GNAQ	G-proteins	PLCB2	Effectors	#00a2ff
+GNAQ	G-proteins	AKAP13	Effectors	#00a2ff
+GNAQ	G-proteins	TUBB3	Effectors	#00a2ff
+GNAQ	G-proteins	TUBB2A	Effectors	#00a2ff
+GNAQ	G-proteins	TUBA1A	Effectors	#00a2ff
+GNAQ	G-proteins	ARHGEF1	Effectors	#00a2ff
+GNAQ	G-proteins	TUBGCP3	Effectors	#00a2ff
+GNAQ	G-proteins	TUBGCP6	Effectors	#00a2ff
+GNAQ	G-proteins	TUBGCP5	Effectors	#00a2ff
+GNAQ	G-proteins	TTC1	Effectors	#00a2ff
+GNAQ	G-proteins	TUBA1C	Effectors	#00a2ff
+GNAQ	G-proteins	TUBGCP2	Effectors	#00a2ff
+GNAQ	G-proteins	TUBB6	Effectors	#00a2ff
+GNAQ	G-proteins	TUBB2B	Effectors	#00a2ff
+GNAQ	G-proteins	TUBB1	Effectors	#00a2ff
+GNAQ	G-proteins	RIC8A	Effectors	#00a2ff
+GNAQ	G-proteins	TUBG2	Effectors	#00a2ff
+GNAQ	G-proteins	TUBA8	Effectors	#00a2ff
+GNAQ	G-proteins	ARHGEF12	Effectors	#00a2ff
+GNAQ	G-proteins	TUBE1	Effectors	#00a2ff
+GNA13	G-proteins	ARHGEF11	Effectors	#00a2ff
+GNA13	G-proteins	CDH1	Effectors	#00a2ff
+GNA13	G-proteins	ARHGEF1	Effectors	#00a2ff
+GNA13	G-proteins	MAP3K5	Effectors	#00a2ff
+GNA13	G-proteins	RIC8A	Effectors	#00a2ff
+GNA13	G-proteins	ARHGEF12	Effectors	#00a2ff
+EDNRB	GPCRs	EDN1	Effectors	#00a2ff
+EDNRB	GPCRs	GRK2	GRKs	#00a2ff
+EDNRB	GPCRs	GRK6	GRKs	#00a2ff
+EDNRB	GPCRs	GNA14	G-proteins	#00a2ff
+EDNRB	GPCRs	GNAI2	G-proteins	#00a2ff
+EDNRB	GPCRs	GNAI3	G-proteins	#00a2ff
+EDNRB	GPCRs	GNA11	G-proteins	#00a2ff
+EDNRB	GPCRs	GNAQ	G-proteins	#00a2ff
+EDNRB	GPCRs	GNAI1	G-proteins	#00a2ff
+EDNRB	GPCRs	GNA13	G-proteins	#00a2ff
+EDNRA	GPCRs	EDN1	Effectors	#ff0000
+EDNRA	GPCRs	GRK6	GRKs	#ff0000
+EDNRA	GPCRs	GNAI2	G-proteins	#ff0000
+EDNRA	GPCRs	GNAI3	G-proteins	#ff0000
+EDNRA	GPCRs	GNA11	G-proteins	#ff0000
+EDNRA	GPCRs	GNAI1	G-proteins	#ff0000
\ No newline at end of file
diff --git a/frontend/public/data/GPCRs_data_node_attributes.tsv b/frontend/public/data/GPCRs_data_node_attributes.tsv
new file mode 100644
index 0000000..6b7c7de
--- /dev/null
+++ b/frontend/public/data/GPCRs_data_node_attributes.tsv
@@ -0,0 +1,11 @@
+Node	Layer	Color
+GNAI3	G-proteins	#2c9c4d
+GNAI1	G-proteins	#2c9c4d
+GNAI2	G-proteins	#2c9c4d
+GNA11	G-proteins	#2c9c4d
+GNA13	G-proteins	#0000ff
+GNA14	G-proteins	#0000ff
+GNAQ	G-proteins	#0000ff
+GRK2	GRKs	#0000ff
+EDNRB	GPCRs	#0000ff
+EDNRA	GPCRs	#ff0000
\ No newline at end of file
diff --git a/frontend/public/data/GPCRs_export.json b/frontend/public/data/GPCRs_export.json
new file mode 100644
index 0000000..5044f32
--- /dev/null
+++ b/frontend/public/data/GPCRs_export.json
@@ -0,0 +1,3083 @@
+{
+    "scene": {
+        "position_x": "-44",
+        "position_y": "154",
+        "scale": "0.525773128978409",
+        "color": "#000000",
+        "rotation_x": "0.471238898038469",
+        "rotation_y": "0.663225115757845",
+        "rotation_z": "-1.60570291183478"
+    },
+    "layers": [
+        {
+            "name": "GPCRs",
+            "position_x": "-518",
+            "position_y": "-300",
+            "position_z": "0",
+            "last_layer_scale": "0.4",
+            "rotation_x": "0",
+            "rotation_y": "0",
+            "rotation_z": "0",
+            "floor_current_color": "#777777",
+            "geometry_parameters_width": "1001.90476190476"
+        },
+        {
+            "name": "GRKs",
+            "position_x": "-206",
+            "position_y": "150",
+            "position_z": "0",
+            "last_layer_scale": "0.3",
+            "rotation_x": "0",
+            "rotation_y": "0",
+            "rotation_z": "0",
+            "floor_current_color": "#f7f43e",
+            "geometry_parameters_width": "1001.90476190476"
+        },
+        {
+            "name": "G-proteins",
+            "position_x": "256",
+            "position_y": "0",
+            "position_z": "0",
+            "last_layer_scale": "1",
+            "rotation_x": "0",
+            "rotation_y": "0",
+            "rotation_z": "0",
+            "floor_current_color": "#777777",
+            "geometry_parameters_width": "1001.90476190476"
+        },
+        {
+            "name": "Effectors",
+            "position_x": "768",
+            "position_y": "0",
+            "position_z": "0",
+            "last_layer_scale": "1.4",
+            "rotation_x": "0",
+            "rotation_y": "0",
+            "rotation_z": "0",
+            "floor_current_color": "#777777",
+            "geometry_parameters_width": "1001.90476190476"
+        }
+    ],
+    "nodes": [
+        {
+            "name": "EDNRB",
+            "layer": "GPCRs",
+            "position_x": "0",
+            "position_y": "-168.952380952381",
+            "position_z": "0",
+            "scale": "1.04054054054054",
+            "color": "#0000ff",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "EDNRA",
+            "layer": "GPCRs",
+            "position_x": "0",
+            "position_y": "168.952380952381",
+            "position_z": "0",
+            "scale": "0.824324324324324",
+            "color": "#ff0000",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GRK2",
+            "layer": "GRKs",
+            "position_x": "0",
+            "position_y": "-126.714285714286",
+            "position_z": "0",
+            "scale": "0.824324324324324",
+            "color": "#0000ff",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GRK6",
+            "layer": "GRKs",
+            "position_x": "0",
+            "position_y": "126.714285714286",
+            "position_z": "0",
+            "scale": "0.662162162162162",
+            "color": "#ce7d78",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GNA14",
+            "layer": "G-proteins",
+            "position_x": "0",
+            "position_y": "-386.047107014849",
+            "position_z": "-218.266666666667",
+            "scale": "0.608108108108108",
+            "color": "#0000ff",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GNAI2",
+            "layer": "G-proteins",
+            "position_x": "0",
+            "position_y": "278.882690597461",
+            "position_z": "354.8",
+            "scale": "2.5",
+            "color": "#2c9c4d",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GNAI3",
+            "layer": "G-proteins",
+            "position_x": "0",
+            "position_y": "-258.878648233487",
+            "position_z": "118.266666666667",
+            "scale": "2.39189189189189",
+            "color": "#2c9c4d",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GNA11",
+            "layer": "G-proteins",
+            "position_x": "0",
+            "position_y": "-367.880184331797",
+            "position_z": "-118.266666666667",
+            "scale": "0.662162162162162",
+            "color": "#2c9c4d",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GNAQ",
+            "layer": "G-proteins",
+            "position_x": "0",
+            "position_y": "-249.713261648745",
+            "position_z": "-168.266666666667",
+            "scale": "2.17567567567568",
+            "color": "#0000ff",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GNAI1",
+            "layer": "G-proteins",
+            "position_x": "0",
+            "position_y": "54.7055811571941",
+            "position_z": "118.266666666667",
+            "scale": "2.44594594594595",
+            "color": "#2c9c4d",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GNA13",
+            "layer": "G-proteins",
+            "position_x": "0",
+            "position_y": "-281.546338965694",
+            "position_z": "-318.266666666667",
+            "scale": "0.878378378378378",
+            "color": "#0000ff",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "EDN1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-588.731182795699",
+            "position_z": "-565.573333333333",
+            "scale": "0.554054054054054",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "ADCY6",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "102.692226967742",
+            "position_z": "297.329921729032",
+            "scale": "0.608108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "GPRIN2",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "201.813275225806",
+            "position_z": "297.329921729032",
+            "scale": "0.608108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBB4A",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "300.934323483871",
+            "position_z": "297.329921729032",
+            "scale": "0.986486486486487",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "NPM1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "400.055371741935",
+            "position_z": "297.329921729032",
+            "scale": "0.878378378378378",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBB",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "499.17642",
+            "position_z": "297.329921729032",
+            "scale": "1.63513513513514",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "HSP90AB1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "3.57117870967747",
+            "position_z": "396.450969987097",
+            "scale": "1.31081081081081",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "RAC2",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "102.692226967742",
+            "position_z": "396.450969987097",
+            "scale": "0.716216216216216",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBG1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "201.813275225806",
+            "position_z": "396.450969987097",
+            "scale": "1.52702702702703",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "MYH9",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "300.934323483871",
+            "position_z": "396.450969987097",
+            "scale": "0.932432432432432",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "CAPZA2",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "400.055371741935",
+            "position_z": "396.450969987097",
+            "scale": "0.608108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "PTEN",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "499.17642",
+            "position_z": "396.450969987097",
+            "scale": "0.986486486486487",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "RAC3",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "3.57117870967747",
+            "position_z": "495.572018245161",
+            "scale": "0.716216216216216",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "RAC1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "102.692226967742",
+            "position_z": "495.572018245161",
+            "scale": "1.14864864864865",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBA1B",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "201.813275225806",
+            "position_z": "495.572018245161",
+            "scale": "1.47297297297297",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBA4A",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "300.934323483871",
+            "position_z": "495.572018245161",
+            "scale": "1.47297297297297",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBB4B",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "22.0215053763442",
+            "position_z": "10.9174193548388",
+            "scale": "1.41891891891892",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBB3",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "102.692226967742",
+            "position_z": "-0.0332230451612645",
+            "scale": "1.52702702702703",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBB2A",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "201.813275225806",
+            "position_z": "-0.0332230451612645",
+            "scale": "1.36486486486486",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "RAP1GAP2",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "300.934323483871",
+            "position_z": "-0.0332230451612645",
+            "scale": "0.608108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBA1A",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "400.055371741935",
+            "position_z": "-0.0332230451612645",
+            "scale": "1.68918918918919",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBGCP3",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "499.17642",
+            "position_z": "-0.0332230451612645",
+            "scale": "1.2027027027027",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "ITCH",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "3.57117870967747",
+            "position_z": "99.0878252129032",
+            "scale": "0.608108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBGCP6",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "102.692226967742",
+            "position_z": "99.0878252129032",
+            "scale": "0.932432432432432",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBGCP5",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "201.813275225806",
+            "position_z": "99.0878252129032",
+            "scale": "0.932432432432432",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TTC1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "300.934323483871",
+            "position_z": "99.0878252129032",
+            "scale": "0.608108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBA1C",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "400.055371741935",
+            "position_z": "99.0878252129032",
+            "scale": "1.47297297297297",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBGCP2",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "499.17642",
+            "position_z": "99.0878252129032",
+            "scale": "1.41891891891892",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBB6",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "3.57117870967747",
+            "position_z": "198.208873470968",
+            "scale": "1.09459459459459",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBB2B",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "102.692226967742",
+            "position_z": "198.208873470968",
+            "scale": "1.09459459459459",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBB1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "201.813275225806",
+            "position_z": "198.208873470968",
+            "scale": "0.932432432432432",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "RIC8A",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "300.934323483871",
+            "position_z": "198.208873470968",
+            "scale": "0.77027027027027",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBG2",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "400.055371741935",
+            "position_z": "198.208873470968",
+            "scale": "0.932432432432432",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBA8",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "499.17642",
+            "position_z": "198.208873470968",
+            "scale": "0.662162162162162",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TUBE1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "3.57117870967747",
+            "position_z": "297.329921729032",
+            "scale": "0.662162162162162",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "YWHAQ",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-360.695340501792",
+            "position_z": "184.426666666667",
+            "scale": "1.2027027027027",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "RANGAP1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-390.920067958327",
+            "position_z": "245.615378207216",
+            "scale": "0.716216216216216",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "YWHAH",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-458.83441408108",
+            "position_z": "260.727741935484",
+            "scale": "0.878378378378378",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "USP33",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-513.297491039427",
+            "position_z": "218.383839728043",
+            "scale": "0.554054054054054",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "TRAF6",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-513.297491039427",
+            "position_z": "150.46949360529",
+            "scale": "1.04054054054054",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "PDE6D",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-458.83441408108",
+            "position_z": "108.125591397849",
+            "scale": "0.5",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "JUN",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-390.920067958327",
+            "position_z": "123.237955126117",
+            "scale": "1.58108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "PLCB2",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "69.0752688172043",
+            "position_z": "-115.573333333333",
+            "scale": "0.662162162162162",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "ARHGEF11",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-154.623655913978",
+            "position_z": "-415.573333333333",
+            "scale": "0.554054054054054",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "PIK3CA",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-195.828078334433",
+            "position_z": "-350.302968866719",
+            "scale": "0.662162162162162",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "PIK3CB",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-300.161338409122",
+            "position_z": "-315.573333333333",
+            "scale": "0.554054054054054",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "ARF6",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-418.804744214072",
+            "position_z": "-327.634809176151",
+            "scale": "0.608108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "AKAP13",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-496.243727598566",
+            "position_z": "-380.843697799947",
+            "scale": "0.554054054054054",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "ARHGEF1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-496.243727598566",
+            "position_z": "-450.302968866719",
+            "scale": "0.608108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "ARHGEF12",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-418.804744214072",
+            "position_z": "-503.511857490515",
+            "scale": "0.608108108108108",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "CDH1",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-300.161338409122",
+            "position_z": "-515.573333333333",
+            "scale": "0.878378378378378",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        },
+        {
+            "name": "MAP3K5",
+            "layer": "Effectors",
+            "position_x": "0",
+            "position_y": "-195.828078334433",
+            "position_z": "-480.843697799947",
+            "scale": "0.716216216216216",
+            "color": "#a48a9e",
+            "url": "",
+            "descr": ""
+        }
+    ],
+    "edges": [
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "EDNRA_GPCRs",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "GRK2_GRKs",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "GRK6_GRKs",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRA_GPCRs",
+            "trg": "GRK6_GRKs",
+            "opacity": "0.3",
+            "color": "#ff0000",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "GNA14_G-proteins",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "GNAI2_G-proteins",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "GNAI3_G-proteins",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "GNA11_G-proteins",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "GNAQ_G-proteins",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "GNAI1_G-proteins",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "GNA13_G-proteins",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRA_GPCRs",
+            "trg": "GNAI2_G-proteins",
+            "opacity": "0.3",
+            "color": "#ff0000",
+            "channel": ""
+        },
+        {
+            "src": "EDNRA_GPCRs",
+            "trg": "GNAI3_G-proteins",
+            "opacity": "0.3",
+            "color": "#ff0000",
+            "channel": ""
+        },
+        {
+            "src": "EDNRA_GPCRs",
+            "trg": "GNA11_G-proteins",
+            "opacity": "0.3",
+            "color": "#ff0000",
+            "channel": ""
+        },
+        {
+            "src": "EDNRA_GPCRs",
+            "trg": "GNAI1_G-proteins",
+            "opacity": "0.3",
+            "color": "#ff0000",
+            "channel": ""
+        },
+        {
+            "src": "EDNRB_GPCRs",
+            "trg": "EDN1_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "EDNRA_GPCRs",
+            "trg": "EDN1_Effectors",
+            "opacity": "0.3",
+            "color": "#ff0000",
+            "channel": ""
+        },
+        {
+            "src": "GRK2_GRKs",
+            "trg": "GRK6_GRKs",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "ADCY6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "GPRIN2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBB4A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "NPM1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBB_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "HSP90AB1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "RAC2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBG1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "MYH9_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "CAPZA2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "PTEN_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "RAC3_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "RAC1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBA1B_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "RAP1GAP2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "ITCH_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBGCP6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBGCP5_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TTC1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBB1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "RIC8A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBG2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBA8_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "TUBE1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "GNAI3_G-proteins",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "GNAI2_G-proteins",
+            "trg": "GNAI1_G-proteins",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "ADCY6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "GPRIN2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBB4A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBB_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "RAC2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBG1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "YWHAQ_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "RANGAP1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "RAC3_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "RAC1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBA1B_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "YWHAH_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "RAP1GAP2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "USP33_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBGCP6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBGCP5_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBB1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "RIC8A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBG2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBA8_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TUBE1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI3_G-proteins",
+            "trg": "GNAI1_G-proteins",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "ADCY6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "PDE6D_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "GPRIN2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBB4A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "JUN_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBB_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "RAC2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBG1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "MYH9_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "RANGAP1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "PTEN_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "RAC3_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "RAC1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBA1B_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "RAP1GAP2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBGCP6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBGCP5_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBB1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "RIC8A_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBG2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBA8_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "TUBE1_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAI1_G-proteins",
+            "trg": "GNA13_G-proteins",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "GNA14_G-proteins",
+            "trg": "GRK2_GRKs",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNA14_G-proteins",
+            "trg": "PLCB2_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNA11_G-proteins",
+            "trg": "GRK2_GRKs",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNA11_G-proteins",
+            "trg": "PLCB2_Effectors",
+            "opacity": "0.3",
+            "color": "#62e84a",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "ARHGEF11_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBB4A_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBB_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBG1_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "GRK2_GRKs",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "PIK3CA_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "PIK3CB_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "ARF6_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBA1B_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "PLCB2_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "AKAP13_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "ARHGEF1_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBGCP6_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBGCP5_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TTC1_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBB1_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "RIC8A_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBG2_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBA8_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "ARHGEF12_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNAQ_G-proteins",
+            "trg": "TUBE1_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNA13_G-proteins",
+            "trg": "ARHGEF11_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNA13_G-proteins",
+            "trg": "CDH1_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNA13_G-proteins",
+            "trg": "ARHGEF1_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNA13_G-proteins",
+            "trg": "MAP3K5_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNA13_G-proteins",
+            "trg": "RIC8A_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "GNA13_G-proteins",
+            "trg": "ARHGEF12_Effectors",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "NPM1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBB_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "HSP90AB1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "YWHAQ_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "MYH9_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "RANGAP1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "RAC1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBA1B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "YWHAH_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "ITCH_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "JUN_Effectors",
+            "trg": "TUBB1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "MYH9_Effectors",
+            "trg": "CAPZA2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "MYH9_Effectors",
+            "trg": "RAC1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "MYH9_Effectors",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "CAPZA2_Effectors",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "PTEN_Effectors",
+            "trg": "TUBA1B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "PTEN_Effectors",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "PTEN_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "PTEN_Effectors",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "PTEN_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB3_Effectors",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB3_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB3_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB3_Effectors",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB3_Effectors",
+            "trg": "TUBB1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1A_Effectors",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1A_Effectors",
+            "trg": "TTC1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1A_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1A_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1A_Effectors",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1A_Effectors",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "ARF6_Effectors",
+            "trg": "RAC1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "ARF6_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "CDH1_Effectors",
+            "trg": "MYH9_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "CDH1_Effectors",
+            "trg": "RANGAP1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "CDH1_Effectors",
+            "trg": "PTEN_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "CDH1_Effectors",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "CDH1_Effectors",
+            "trg": "ARHGEF12_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "HSP90AB1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "CDH1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBG1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "GRK2_GRKs",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "YWHAQ_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "PTEN_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBA1B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "MYH9_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "PIK3CB_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "TUBA1B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "YWHAH_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "MAP3K5_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAQ_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "PIK3CA_Effectors",
+            "trg": "RAC1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "PIK3CA_Effectors",
+            "trg": "YWHAH_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "PIK3CA_Effectors",
+            "trg": "ARHGEF1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1C_Effectors",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1C_Effectors",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB6_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "NPM1_Effectors",
+            "trg": "HSP90AB1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "NPM1_Effectors",
+            "trg": "YWHAQ_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "NPM1_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "NPM1_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "NPM1_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "NPM1_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "RAC1_Effectors",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "RAC1_Effectors",
+            "trg": "PLCB2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "RAC1_Effectors",
+            "trg": "AKAP13_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "RAC1_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "ITCH_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "CDH1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "TUBG1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "GRK2_GRKs",
+            "opacity": "0.3",
+            "color": "#00a2ff",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "MYH9_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "GRK6_GRKs",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "PTEN_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "MAP3K5_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "HSP90AB1_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "MAP3K5_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4A_Effectors",
+            "trg": "JUN_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4A_Effectors",
+            "trg": "TUBB_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4A_Effectors",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4A_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4A_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4A_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB2B_Effectors",
+            "trg": "TUBB1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB2B_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBA1B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "YWHAH_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBGCP6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBGCP5_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBB1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBG1_Effectors",
+            "trg": "TUBG2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB2A_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB2A_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB2A_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB2A_Effectors",
+            "trg": "RIC8A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBA4A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA1B_Effectors",
+            "trg": "TUBB1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAH_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAH_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "YWHAH_Effectors",
+            "trg": "MAP3K5_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA4A_Effectors",
+            "trg": "TUBB4B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA4A_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA4A_Effectors",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA4A_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA4A_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA4A_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA4A_Effectors",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBA4A_Effectors",
+            "trg": "TUBB2B_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP2_Effectors",
+            "trg": "TUBG2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4B_Effectors",
+            "trg": "TUBB3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4B_Effectors",
+            "trg": "TUBB2A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4B_Effectors",
+            "trg": "TUBA1A_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4B_Effectors",
+            "trg": "TUBGCP3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4B_Effectors",
+            "trg": "TUBA1C_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4B_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBB4B_Effectors",
+            "trg": "TUBB6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "RAC2_Effectors",
+            "trg": "RAC3_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "RAC2_Effectors",
+            "trg": "RAC1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "RAC3_Effectors",
+            "trg": "RAC1_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP3_Effectors",
+            "trg": "TUBGCP6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP3_Effectors",
+            "trg": "TUBGCP5_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP3_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP3_Effectors",
+            "trg": "TUBG2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP6_Effectors",
+            "trg": "TUBGCP5_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP6_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP6_Effectors",
+            "trg": "TUBG2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP5_Effectors",
+            "trg": "TUBGCP2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "TUBGCP5_Effectors",
+            "trg": "TUBG2_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "RANGAP1_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        },
+        {
+            "src": "USP33_Effectors",
+            "trg": "TRAF6_Effectors",
+            "opacity": "0.3",
+            "color": "#cfcfcf",
+            "channel": ""
+        }
+    ],
+    "universalLabelColor": "#ffffff"
+}
\ No newline at end of file
diff --git a/frontend/public/data/StringApp_aspirin.cys b/frontend/public/data/StringApp_aspirin.cys
new file mode 100644
index 0000000000000000000000000000000000000000..17c7a557434d83d175dc57f709a723e0223f609e
GIT binary patch
literal 1080142
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.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
z%0A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

Hn*EeehFf+0CF~;?=E2zNVhvx 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

geWsZ=;RDaq18@eiQzw9`k)Xc2+pW>_cRfKIn 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 Z4t(}6r z)hiq{C~?9h_ zBC3sBXedZ_?hYs93Ap1tQxA$JOu78pJToO3w{;aKoyfCn9NmeW*ocxd<4z%B#2Hvt zx|D6g+jeg!wxRhC?mgDjcj#Bx5NJ!mR52h%<`8<4^A{6N0{9 z6cy3CqY?*AEeZhbOUB6}3Qt;&h3Dt>n1fY@VlH7^TAmyYFC?(Jd+%`yH{2Cr&FY83uF0P}Y($zc2PW8VAEW&|5gB;3-jP>pG8I;Bdt4ffrj6 zjUNpjN9J06hd+LAU8g2mMjD+v`6L>?T&J#)*1PJ3q$PnocMV&tJbosv9L4&Feh=oB z&lmOfN9dprc|cIrn%W5{IdHdN@F|v(UBV+6EWZ%7AtgFBC@Y(-#7jiwQ!iilD8m~Y zMU8tWMz9-vjdYx;c$2x%@}ze5;{iAOMFJLg-LWi{s&~@)ho9`omm4+6>_9-kV2s)b zK_(zcnqhfkqTknJ!lGH4lnG4y&5^9XTAueTv^@APRuOI{NDluR6Vr}+*YgW-7e%%< zN=qh19dQ=YicBs6oo1(l|pmb#+bPzj@F3siai1MMeMT?Y$@xIn(`PS z3k`&RcG{C04OBtl@7b=cssIs<$gB2rq~$DVM%I&s##Fx{q5Idp5?IIxKi0hd;aP3t zUuxb+PwGkA9=jX`ToR)ETFxnA#K3oEs54I;T?Y-k5$_b}NXv<>#r95WovTs(!hglX zOBbrP$Z+{4EnhOcDA{TmXVR{$4U|%AoawiF7kiTOh8sj?EUpxSRT+!+u$KRfosJBW zGMx^yhaD}_l2dpEcT})uCmrqs(ND34v0d4XUX1m4&*t>P5KZat_~iQPeB8%OA-H!c z){o`SJ+pc}@!85gzRs!5RX-&8pn}jYT8+d5^GxaPVBF3v{s}Gp@E20}iv{6Q3P?Wd^NY7Wr`3J_?12gr`BnH4<29{z zoI9i@VL&P=`CukL(-9?HTUIx>#8BD5%RHoduF{pN8yA?ev%|KneaLkp@@WbidP-tw z5F6Cl-de!z`Gp^Q4L3_^`HnTd=SSmI>n@IYl*ru|(YP1c%Qw9W7-SW&{-;(x94{xM zUM(|roE9X;c*D)YnB?WC6sIY>loOcXmFhqIYQZ%8FtWA|!uw>do`*+Ri&NtAeEI3r zP~X|?g?DdGEyG4rDZQjtJtpF}v-x%V^A~3C*cH@^xj+DokZ!0~dPwsGc??t*As#h* zUxgr%Or|fZ%R$-9y0YkH?eO#;`EAxMJkPgG z1!^q=G3keK$HXXp^JSKJ}^QV|3iXQoQ+LBuBPEv?E7%oC*mPy8|cr2?_4 zZN|n>{V^^YvLq84{GK7spS)+xApCvrfcbuq9_ii7M-&SD|>t0a_}tCpS)%0DsE+(hPn($ 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

3Ux2~A4gmjdBzGIx%{};7m&JbVF2iX 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

0v5PkB&CQP5+b)MiPf;~B34U`w~k-LG{ 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 `8) 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 literal 0 HcmV?d00001 diff --git a/frontend/public/images/help/cytoscape_aspirin.png b/frontend/public/images/help/cytoscape_aspirin.png new file mode 100644 index 0000000000000000000000000000000000000000..ff000873c2932ff4d52be08aa75a3d5f32fbc619 GIT binary patch literal 348759 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℘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

FphZ^vPRIdG6Mn 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 V?%NX1{0sQVN-4fAe)G}qe*izxgX{nR literal 0 HcmV?d00001 diff --git a/frontend/public/images/help/arena_cytoscape_aspirin.png b/frontend/public/images/help/arena_cytoscape_aspirin.png new file mode 100644 index 0000000000000000000000000000000000000000..789fd0ac2cad44211607cb985cef2410c0a5f851 GIT binary patch literal 136329 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 z zb-Ac&_N@eBe*5jWOy4YP(KWLGMn<8GX2OD0f)E9dBCKQpTEJH-jcgis7xmthKKC~5 z+_oLsOTdzXKS*Kc{b|E3Z}>>HTAXU#ZLr4>shVq6(ZD?qt;Cx7)VwU~nF*Kz32Vpn z230vJY}~94Q~UV$aLf~J1X~JWY6jM10%nefjM0A#O^$++tDLmexP9jy|5c-mS{?)S z^KfkA;QdCoGp~h9%2vN09lAp0{Cf2zRjMEQTm5S)|5<9U2!pQ{KdEgLralc_Gke$F z68-a?-;N7oO&zbfW5KfiHFm@{m_68MQsfigAeFtn#gr9j&lU4`XB|CXW6CPanOmA9 zX9mVi)*6deX!M~4JXzteCi0z~(D6OTc^RV|e0NN38l+zX$VzTODC?i3=TitJP#_d0 zAd&;XE0HgOgMCI-+>z-0@_w?lPiMMa(SX(`@50q~& z_(3JsENETPGm!6W8icbLPOoF)#$?ROAl|>h{ba+-3|j{dgbp3eAd|g*Hl`M4Wig94 zun+#qYs!~iQ){>yqAuL1+xHlt?XxCj`YfYUwSJi;a$E+yrb?}RuV!YIpH9AMbwL}D zuJJNN*y2M7ECquV;BJG<`Ks_A?q2%k-kqGwmwvl->(;SV35)yH+!S4Z_F#1A0gm>M z#izo?dPuTu*51&qr`H@j-fhi6?V4X3tvcpEY^*wuOG+{8sv=@d;wRvs1`4G20%!_? zHIbpLh!8;f)kW8Z+Gi91E(xf~S{Ysw&`GAFRn>6SZ5G3HNb+F`qzJdUr*l~SvPXG=N{U3C?P(9 zsvg@v;a-|_dz!Rv&_nkt1RWNp>XmtJXk^p$pF zv`xFdV6aIzpRsc?9PjM1d$?Sc(7g!#Q)_2mv-Q@u!9d=*HX8RLl)xuR z^aDu<24HBjJmxPP3I;+fk}*=^DI~<5_{;kc+ejoJgnkmCGg~dDb7x@z!+Q+X~=Iega8=j9o ztlnYf%nYzofQ~}vF}PY9Kg$4Xs@1AGHl6-h_qX@$ z&S-tcst|}bXWK{$62Vz5q z4*o*{%_kJ3PS6m^m`~(K(g6zT`GnF$d5wt(92?XorjziR$Y{KRu%1MoVo3>X!cPud zXkbG^QS|-(L8>ET6&KUob06xWS&1N>L}d~l>*Be*IhL9TKEy6}F$yS?!9_R-l*oWu z*a)=nM7>3Ue)Q zb9b-VKQS>CtQp%e;Y^}*WVDnyaA$nqS*}jc4F3z#xcTPscJXc*(1Nj*Z_a{bsPseE z$AzVFY#eLaXj*T5zst2(TmSM(n^D^4{WJtie1QqtC3W)T;Q8b2_Rmx|da{{wq25QO zvHx$tmbZPS*WB(ZHP5Zb3SJKCZ&=Cydz_f{1cYM7Va`Rwf%XttI&SZ=*EaJJQ#L=D zvLQ0jp^F>&i!z;)0_ZVdbs#TK;zrN{D>weKat83BaEFir;G*z>bpns_6ST(`XoygN zAPJ#|)`-Ma6soMlNw@=oC=h)JUKZP^deG#MI0>9ZCa|!FQy@)9PL5r(pu4m`Uqu~}8*y@&qFL<<+q0r4H^Lk4+ha(dX)ld3w-J-+r^WaD5M zs|ETU)ge8Y&cPFTfv2~W*1u(fHFYnqR5>ZtPHK&j+T$vtk>w@>IK<0#N&i|~hBR0+ z#{0zH^=Mkpoj-qMY0S#-y1Ry3%^2)AtVdH~xvQL9>rYy0HEXNG(j)z6WbNLw$9v{# z^y!vUR{D*d8l0HQGPVS(tioYU3>@*)CW;WC5Q&4_DInHF7yd{2M!*Lh%io*>$iUS;Wg0!4(&Wt z+Mshh-Gr$ni&x}GH3RK z+f${*v8$8ArpX?P@?FI_a1LF50fQi zr<|EERZ6YlhTt?RYZ@`d)~;Qf4sG;7av>#hP-a-u%-b@I1yUzZa}UMh{^I0WNJbHg=pi^LozhQS0~Cov}fgxji5; z&2L~VIukQbEbOIAV+ZKUhRarT;_&DUk+y0Nf^C(t$79iu#KmYvMXP*&MlRYX) z80V0f+_?nM6bYGw!~o9;N|N|%LiG#^MBxrM3Gax3Cdg8#;}KsZ#xKJY#EFL`=n$BQ zBou5S1&SmvA~KWm<-||UlN0;^a%H7Wv7lAf{J>CHPk=1^C@cWz0x@n571(Jt(VaYY zPmUft0y)V;jW!r~-L0;**H6#w96?T)t?#`u-)eH-BjQo-(0;%kSs81zoP1AKVoj4( z)GNAWh$pN*1--FTngM)*aECD6-Mf#b39y$lW5s<2X zGVd|HjeB`0mzZuLrOfrJy!RLB%nQewd}FIttr)lBV*@;Fmky}2E4KFKgR6eovxYhN z?3purXZKnYS$FS9>&b%x`?q^M#VQp#W6g#8U9)y|nv!|*&fUZ*(}L%3_E~n&Z_sdi zw-VMZRyeHr&F7yiT$qJ=`rNs5sAgTJ zju~;hFyfLCM;?}WXzj@f(q+@2D_5`WO$k{)s8(9f`ZoE^Ux@W;)vA@qWN-Al3R1Ch z9z1?R+l6LN$sXKyu$!|RP@YetR?%2JjZ^U?FYDfz&aoGdTpTfI1S{3}g!s5Yai`{; z${vx8ySiLpi5zHDn!sZ%njLT6c)hRIyAo@bNM|YaLQG;!wYuj2Y7H4W1do&w=^n)? z#Gy-jZ|S66?kYv*z#?Hy+aH=tdAqsvme<|7+OCfgQ4wE&R0{LT zSFoyoN_sjt*x5U{xwIY}8W}MVT@%kmoRY{X5=AxjZQ91sd%)M~K3~~3rEou;il0g# z`G<7+qQyC4YW~gZVO+vwAB@T!ISS&21`SC_roF5WR_j>nW)0aJ#xiniC_nm&h!X zS7zy6Nu_My;`v$osDQ*d$4;IiiUP}P&`iPK7IG0ODGKK=LJfWtgognr3wkV=`O88d z^GzfmC=fWyf*u_Z=9}oaMSM-8m<$o?tg+g-b`^G(Z2W^N91Q>{3IvHPMrDJ%=823K z*3$xO(mjycKvD)5JSbR}W4wZn{c>VSgbi%>giduU7S^O8fSYV;z+a9L3y`~5>0pnA zHsiTw(yBKlYd*;hL;_I0{r20ReDVooCzXcaBC*&8dO|U< zMn$&{#6-z1a3_#Ju$2&vWMljS2Vox(iBLsC6^>3EFbX7vp+r#w358T7aljN$W)KB7 za&qD)B3DijsDM1kDZr(iK(su9(jZVpLP(0jX=qXYu(XuHH+KxWx-or@PKPe5@`0zw zYLVKsYZt3+HoR`bsG4pAsyp`n$pMwUNX`74eeHi3tXakBW=!lz!hJvk6}G+f^H*X` z{n~hT+(_e<;uJTCjPU#sL+_;^A4jYBehlbdH7-iGB{X&N&7)NT2j()8~T3-+sQBLjQWPSd&VlH!~pR z`t);CEDx`DV6^r9>t~611lBxr`sDi29&01(Z5^hZ92pqe+Dj*|G#c%ov2Kg^H15_D zvl{g5nG3Ujp0rG{^jPCL8w1BoB%!CxDow}_L*r`gal^If%8P|Tfe1j;Ph(hq0!8@4wDY_yHYx3xNb%ze9pZ3_!&yju)T= zmi3gtYmkA5GJ?R(5U5C42+@m|F0C8oxwv1g^HJyN zUv1^r@_w3>lPVpXe{SXc^%?Q=&yACg$4P6WFUEzYH*pUr^1Q466!}1q2B|gvXH+{2vKf^c0V^h4*1%0s@M}u}GX}b&a5d2@(mM z2ZAJlFG+wu7T$nD*d-(tiW-*3=tU7?{Nxd6M@c|QkR?(MZ3ZQP1rf7ASfqi0UFI{+ z(!qhNMiW2BycFMmFVl~~hw1vzLOCOD0r;hLP%Fd;o^ zYEnWHvnf+7v+0Dy3CnsfnCuik&3*ispkb57BtLGl=gL9hHK1$MtTBt{P0sT5^VPq` z{EAVU!9=2;fn#`V=py|Ynq&Ga_Zs9Z*`F^KYw|5HI;=RwM==h%G_}s|S8HW>&C8o7 z-nn@d->ly*o}b_KJ&ZV43|3E%4(b}>p;FjbMJHQl?$EA0(PsRN?gPRv{q|djlsUDh zthAW2%6;6N7UO2H_f`p2l*+SiyH4-U>Hd!_SF7ii5JfJ(RIz4#k2Y1JrB^0ORVH7$ zaO3vI&6|O7EN~0{w)AbddBhX9aLghb7@^vichBwGKSnvCgK5xB zA<)mK0om{POK*AJ`Oz&wU5sTAGbte~s#dMawm=+^kW?a3jRuLODZ(vpA{%R9R6QtU zz@6YlP$r9Kl2~lRYhv>Q)&#Z!Phl=$H<{e!#7}Nvu1Ag`_du>NN`l}-N+Saij}(Qj z11t+{K>Wgkk_Xhwz2=f%fcJ^z_R<=g^V3I;i$|^kK*6IA6vQt8udvaWh;d7?UMzv>9N;9{%*?li>8=Si%{nV(i zF+xEm@~{@6vGooA4en)%r9aVw4bC&m$~es6Qc3-Mu~?Ha#wt+%TYOv<+5PM89ns+A z^7wmqbI{nH|MkS89{*lHsNRYpPGHTzCN63#i~8fTbdEtY)=yozF?H=024`r@sM=H4 zSkBzqBzbOdQkuPciJ5E$d^x!DKQp_$Gtb$o2ea_q_u0Ub<#Zh5&h=wbPw- z>L6WMi6W#1PA%UKeDsedw{0}VG8zGEg1s;h5()W+SimI+#~?yypg^ZG-+lL8V!4~& z%JRn_e-vMVu+mTRry&4QB{8)j*--Kz+4u{N1m}Sic`gQ&Kmsr;w{w6jW%dVjk`wL| zf|GlBNhAXcen2We35O)~=9Gy5 zE8V5X2f8=Zs2eJ@pw*mGch@7PJry2UyNhypQMb#`(5y6LVreWbs7OUqX8M4rnZ zRkVXjtZA~|*bm)NLoaYZ++(MYMH)JEuqIt6uFt4xZsX?qP95HA$f9|R=(Pr=^!ANk z8Fp_)|GQhJUdR|d$IZ!2&H!UiH!{Wakc~^MWAbJIebTq&a!TXi1!7HV7;cR2sQ+Do zo{EKiYj29SI2!+B&h^WeE}Y&vdq{R@jjcoLuO03=GbY5v(GHVD>z+|ov$ts$?GKJm z-SW$+$zaXC{StPba9MG*SyFn#E+trLD);&!UEZDF^`G;ctQ+aSYLSmi6>F+hitpNv zdwue?8pGz_xP2EONEgz#11`vGWADK&NGPNj`tf-i55Jatze=jqW%=3JtGBY?rb{y} zkcMDB6x_HI5gmgAlmG}#R#tgCvv@8NpW8v1@S3R6c|B}YaQZ>lBV$cE1}8-cya3B< zXg?4tkJz+n6OxhJiBu2bEa0-cJY!8H$((7k@2$JXuIMKhoJ=1%!_Dc5XCb>#Mz_k#3_!se;Onk51#%@k4ruqN=v$w}ktskS#B z?XCcwN@d~P?k6`wixenAu&Dy|?KS`Mm%rfag9#>XKqQz>rXgXhiDW~pp=|&WBCI$m zz$hkd$ZdE~yf_%MkWC0Eq!Z~emea#?kKaI?A?Cekxoq-*0w?Rf^eW|+ji<*qy2@)_L}#H#*9Rg z>)%_1<4UYqph{zP0q(K+|1-J3c2x;nT(+pz2~D5a%m@1^~2tc`fEBI!{2 z=-EyV&%h{|S(F&$*Z;{9gsGjLp?{KNs8Fcyyyp$F73b#*#F`9`yhz&JK{>luo$WF8 zuPz(#;QpO!moKgvAGoxC?Tu_26xA4B)5$@{D6>JOX5u23MSJ@sPP%a60?R>4J9_+= zahX{%EmWLp(Z5%tvTyx;T(>W0+bfJOtPv}2F7Dl1bZ*wrFkq^FIr4GoVoe^SQhrjS z!GM8Lw{PF3-=csJsB^p8G3qbXu6L_pusnm!=IH{(+o^3kxLZ>xi$0(bJ4 zcNWK-M0Sw}XaTzAE^de2q%?|UUCmzrD8Lk;35Z01qx%LV=$`sG@lehPgeu@Eg|zA0 zv+IdveR4KNPfKLM4<4mV)cqh-@k)NxExP~V+n`t9T|8m>{sa3LE?Q`1{UoL@FNTwR zwI;iDSkv}&&z?FzB=OFLt!g=cCDxRy!sG#6ioh;3ErboaLg;SLjO;7T0h24;MgM-4 zS~0fW;thkZWRF`PJz$9bCAy%QB?9FDDCuzew}@jdL5LbeLdl!0IA>lI)?{@^NCi|F z1~2dCq?y&rVtY)33zO>J&AD;)()qO`T~`gPyMMIptfBrRdNpML>u%v6t;()h^MeNt zU?#}2ET~1x8Hdib*m}On*i=XF5;kR1YHYMB%+du0FnDEkBNva&!=~SxvpcQdXnorW z!+cMFmpax|tFbgfJ&kWZiyZcK1j#*pc!B;!#9)Dhf`%TLK4Zq1nOmyFN>#>46W08a zv1ZrY%uI}r*~A=vk)<|tCL)p;@A#EtwFw-gKO04X@C3XS`XE?Ia4<@6+=Eb;lV2Ze z(ksxF%UF|8npml0Ur!WI-U@ii@(;9%-MzsWH1%!e8Ec}tnmcpu*+pliEz;r4!|5Yt zc)C7mEK8dPYm>KKNs%68O7}A)NKLj8+8%ds+<>WWPDaFqsn35==PR+M0emU(pDzv8 zEP*auSgdKPvA+B6J46$UZD1mdAS@&6XD}ad6EP_8A{+{UkiR5BNrDSee3KK{hpYqn zk-!y5Fp>;FIhG5DTO~6{5ui!Q(43ShypH%w0)z-Jb?8rydvxm_FgMvw+HC!BL9aRS zU}7*RxARJrAaH6}=kCKBG>rVwZri#IPy-A6{jR8RK&)by) zg97J7`QUYaZk)w~oJ$9GZeAKuZ*64VZLzi~kwF7HKGtF@ZB_k-X&3AYT6Lgz>_|v2 z7iT9`o9>nwn{2c8H%(pa7d={|EtXO9^Oa=o7h^QKMc3S^8y-$sw>mt@wgCIVl>yfL z?6c4CI02KPeWrd2i#7RRZbVcehQM}liQ_}oNlvSz!3#D`oH-Ao5jcp;IXk2yZGnKS zp`kfB1F6Y#>CbtwCT)$hMMQGTojZ2`ra)B~O2jDn^|5AN9Xd-sMj`|%pp(!9c*LWB zN4u1_{}^qm@{Bc+71UV2?)}beJCimxEu>k9{0Z>88<%f7JK-w32jPK!eu8v1?Q+(L z#ci9lE8vIAMZJ)NO01dBL{;p|l~}Xj9lNBkCRMs*Hegyw{70{H<( zL7YHFff_-F@Rxv5=0Qw3gM)(+kAfBj#ezh+9gKnOLYB%2cuJWpP|<~Q3q=Ve3fv6i zzoj#~Nc#gHtRB52jiM+=6vFKgWW18Ai)-6D+IZg%S()<=A37Wz`2X2E54b9>ykTQ@ zvzyK4-Rzs}CYvwY-fT~gMvV<=x6*qR1q4(S1$)KbK@p@1Nbf~OiUk!Fd+$A#6r-`H z+2(t2^op0u&G?>tx`}8WkSVG@*4tc-cH|~ zIdUfG*-%Q=@pfQMZUM~fB z3LD8Hy%;u{uprV+6yJO%d>RlZ$5rnu?r;PlJ$6g>4))isToQ!)$Th(%>@U?=6Vnm& z0Adb20BFmNFb~C@h$T0{O>8}(RUlR4SEF`ce(D=*s?0oiM^-||{D8q@e)xPd^V)SB zR2${6IA-yT!1*`Qg`1he^>krXcuma2G<)mXDEIom8CbK$Fxf~32G-R5D`;!h)a6O& zuh>Kw<=Y1w=?`H?xDb&i_y{~8mI4W2P*kaq16m;d(iNi+N6$`20YP#hn@FN$NMB8E zsG%H&26|sv?|*;Gz#lh^nma`_=p+ZGp;(85&k)1>#{i?hdUrH+ zbaF&Ai!FddXxVT23cvNZXFuVFMA=gR1Osa}*eXncVU{MIz=O3q|_4d2p?)yVzr$Q-6j}K7L3A#cL!FeoocP0?ZlcEGUa?L zhm9_7dpzCu_ldX^xu9a9(oQ{C8s7|%F@B)^nyH^1TWt1Sai8^bEvjd9KeVX#=18a9 zdH#G}ytvffspryF_@SHSY&`nq#qhWUY`M+6{JLjuuvmG(J9WjNv}N!`YZXzW`MBFT zo?B9RJ7?R3{zFw5q2;P}U`^&l_s*`vT>y$t#`)lyjOnzcIEA|MP>cm`Z2nw(&+P!fi)XJ#TK77 zu%_zF7zjK;M^4HCWMN%mK*>ZOz?5X+_$Pa$9HU!&xkP30f=4U8IpHWguF~#EPU*aT!VJ)DvSR+LClllr4cS~GU}qp#8)H=0zUMikmNh5c`@knfb+C1J+KDw4 zGDWPV)pA>_6}EP39o-N08I~97Im%Pb0Fv&2(bqxQyL@8Tt#f*Ancb6xVOX8=6$nd4B zozsfc!p_~Eh>zG%ChfqQ$Q^0A^icFf=+IH(H1seH9j9%JA-9OT4h+GCpdS4cLQPzG z(8yqLNqu2V$t|!m!A}T3ak?|m5VZh!S2t%uW@4$SP+HCiFyA(($F2pR+5UuwOWfbBW1N$~@t)-79O zA+ca=Tex6hxNLf%L+Y}=S*%@2?Q%U07kdkhtce($Az5_~(o?k!1=nQCqQ$z@!Oi3C zSvOype5aGgBvpp3yVFjrDUm70DUoYhm)qHt+q>`d8k!r@cTzvKj{zjY@=qo)FB;no z%H&^V*k}6|^!m1P!m`*g!M=4=hxSg&$aJU4vtv@0VlN15h6D%K96D<2>LN9J=vddf z1*JVVbQtK7aa7cvOg!5TtchHc#dRPRr0h(V#73*jv(-&Q*F*cnPaMA-SQD8dNANUa zGELWp+!TTo(Gwd=T!nBjr>Qfu!WNSRs;u}-&;c<4V_C+ipk5EE^>6p@-X>2p@1iv` zot=s`xeJdieq8)zT@9vWc!05H8djmKRQc7}<3H|O>QAIq-8NodPu30U=j?r@;)<|c zxLI?vD5}ue)+sq98P9q;0Z3hKMvBZ>bwn3=xo(BgeyI$s*$67O__TpFb#KkjA8TrO zcd1k{VxZOSYUy{&Wp0mqU{y+~TyFFJXHx=)qIybAODjvx^7%tY77i6VTaw9T<{!D1 z{;=Her`<}AywY`Y}|?x_huQAvqONLEk?#x07?b0d&@T!wpM z{IKXjt^>sDQT%C?N2=LtVvxZATwkR~Cd@DxYx!IMfv9_Qi};Hef4x2CdEr@GR1-e)ZNU!yc zHANN#@-VnmnHo2#&CoL8yqh;~qUVJR5h%iiq9Bur7IIT0i!dTaCTGr^!Aui*CxyTz zSFc`0|Esze)}nkYd;$oihj?1-U0{4D`p>Wu*ZG5w;4~tF9MB=3B~@SWv<1%uq`%X{K@^kCx&x{zQ#IB^vdv&A4xFkq;tqCc*h#1z zi2S9s>aYp?#t#zawSW_OTaM* z!4;YjUu{W9@0wXXPsEy?T4G%kJ9g;M;K#3wQ(aW9WbG;0#tZw;o?pFotzAF=UWqGv zWp9)gYz>G>xAh5loVZ=~^p-W@KZK6NW^YC@fIi3?3113I0);RudKNV41lmBvh}9;a z0E%7q0Y@|d1hK*h9akPC&3rhjHc!9y(MKPN4}cMR<>JBDx5twq8`^L#!IZNuYcl1i z`(+Wb0MRTiL9BWY;O;C>DWA{@D~l0crW#CGG`*+(u%?N*Np5(~Py2p4vHC=EcygDn zT@V1%>BE}vG=-pge9#>F)Ho4UrP1>p18X*diY-2EU`^dWf&UiP6iY-jHhZ#klorwn zKmTy(FW+@}`R}u5&7#Lf6%dGM+So@ude;2#+n$SA)c(C3_8KTtga+OOv4C` z0+F2AVdGjPqxCOuts`yDni83Ot|exgE?eA#D~4@8R)Z69%+@&X?LNK{e%4Obsu!Wr z<3vSqiARjhWkbBAnG-E1205~*Y~3=H+S>I>S%u5Fq;Olw_I>FY>9*c}y%I}%W>w3w z*29`kLx!ueZJ!-|V@>={Zhrp*GZ}0ykxDj?HF+s8=81_WT&#~ zdOM+2hta8m_G!RvaTfz?wiqTG$v_)xcI-TVUid=;_qM3rXD!eFF4ojkJC^3sF~9x< z)(q|{CHNm}{oOVYb;L>r9N zFkIuWT6Wl_q5f7KgjS(X)lbz9vKHLIR6oTXMpkS+RZILaHsdSPP(UxWYn%78%~?|_ zlTTFQ59G1KYfSmL>V4HSOJ_S&Ik_M33MwArH?zM=v`J|*)1pUEdlAY6^1yvWbzG{n z?3GYzoVUel&5_KFJ2EmeY^^N&MW>suKH;)rXaA@aMSBH2QazGhv8F^}`R;_`moq*e zK63$}iR(%8Sd)^6CJ{a2_k-0Y>q#PR=Px>6P!g;J0a5ZopHLum#Fw&S#R^OeS$Y&n zB!xo`q!BHW+2^yu>4z#N4Aa4b2hpJu@dvRh7uJ<)VsoNmO)TGubHrDG7!`vPoY1db zz4~?4qGO9q`@85U*3`S0M`E*~K&Tr0L(yO`#X2GRgif(Jz@DL|g&C9MZ5`X;h$72ld zB2u+9{6u)kYB%C>xx|EJtl31{HI;EEN03X^$FKUiHTy`LvnEfbl-XxHO*%Ar>yfP) z=^3F#!xbe~PFq}qN`?$c36_{XG%KMr5SXdnz3v+UBFK0iA0Z^fh{=kYul+Wk^o~g2 zl(oIXyyYAEZa(8SbD{icbguftnsQ0+zl~aFlqMME3rX8fZQFNv+O(XEQOvtgh>LeX zSu8qSGd+VfMY$#v3rdo$M%E;T9C~^D=;2ztg19eoP5j?4e)El&o3-u~rawCLx`j>W zK%9IZ51(Uek8NGHb^65Gpm(YSNIG>7RQ?DV2FF{$S{Z7XpuU z2#!(MdFfT7cxn%0U`>jiMPU@?lHnntzJ7kw|7;%oi~j`t@?ULNB?i{iR<_;>&osN( za<_sdL5qjD46^^sI?HQGLjNdVCkMn^8-{aErq6!9i(nd;dR- z+4&#;_)(*Vt6Xu#^r)3lO}43Qqdiy??>41uV9-E_0l9tNmORI57x%+`hU5+Po8vEi zWLd)U33ZM9>XsNFf*`H#5t*HXY)Ps0?rS5mN~X<=u#C<&D?4Pf=D2@shK<+bfKyLg z(w0B;hczt}mamV$*dazR$`h*2Uai`(5A#Yq6lURMllo zQHRc~150JZdq6EhP@ogBH!B7qXBCB}y1$OzvSw*n>6a^OSEszZ`f|bI{P?Ii(b#}j z<6BToS#^I?y-I0d%|=kM#iup0rqo0}c<<{YuD&qnbcZ2PihkqO&EoZ{k+j>x7+6zP z)U)Tz8R+Ic=9Q0!{_;OQzx=1_c&qOiShK+*CD8PAs|gu4@%i>C$$_yeB8!$t7v(!F zE$EvT88qM7MqRqe7=}JkEjw6~F;WCXMh?c~`hnb8{ro}5B#F7w_E!^c8BGz)=N%h1 zGzdXX-Q0*cQ`b(h?y1(=Zy(mAG_Vc8G3_wdR{y>-)!Mx{aRH{(2+*VNIFT{QpAN z7$pfkDuvQR=kivR1Aoodp--PaEv4pMk9GGw@{do7h?zcP2J83Wy@;R`^G$Z(Kdfx| z%cO{i?zDES9ng1E(R>;JlUQlgELMq>5<4S3+XQ;vl!jR+wTYpd{vKs^LLA2uG^qb0lB@R+B0*Ofoz_;3{cvYY%Z{pnr@y zrjV&G+vQo0WZxc4Wzj8=J?PbWJ11Pd8d{*C(rP68hXta?{qFCn> zz)75H#Y`C!LqZ@iK|*qghF!FXV9Hc?svxrF#7SYdZ{IiWqv#ofL1PJrg!U`bfM35s6mKF*tqp%;q&3A%U$)qN#OvD|W zhtxn;EkVnFwl!kIRV*n-Yd22tr>N8WRF>O%(AJ=m=1fLLnO8NZnpj zCbRJJG0EO2S$)E3*A>U&tN_XmzUL_ zGzPh5Tf=ZetSOVol`iI1o)V?H^_Kp$j`n^M18Y*^K3z>lzx3hYU;byvuihQ@j~>`= z65YKCH6fKb8JoLx(>zSq&&R-;^~>b(5qyKJWR?-uGZ1HH*(M|gM6dA7iSu7L$ZbIV zsHa@U7>WPHzkPlo`NKmNhTOh=8wVh`BswbEO8GccERsujLXFy0&#%E= zQ(|E@c2=*fYKw{^wF1tpYDL~=|Cn@pzd+Sh?YX08tSRGpM0-j^fY?wrk2Pt5GiJ~O=rIT>6o`VTh?7y zrQs?zux7($^XQDPqA#q8rRJib`EkLEaJGGPM*V**EFsIfN9(dCgB;^W-LnjzNPS3C z_aHWQ532HsV`$#R5%-Cst^K2ISyLiY=321~y5~;sVTGY15=UOWauxDeoV$4R$}xSW z^^vkh(1VSzP+G!>qFYzDTa`-9LMB3)5C3IVTW3`TBxE}btP}FRwaZFNe)`6mfEqe( z^vgY@)ZDutYC>y zACDiRMPk{BY!VH=+FZ-lQd&H=c2TI(;jjJl-Bsk-c(RjBWKAqV$(g8}ls0$40F)Wm z_3PJ(wRHaMsXKd<3KFI@HEN)~v8JWc^6G}G!XDxH%H#P_`A+t=FH6rrU%|UmT+~;c z>8O@=r`M+ptZ86P{b`^E)~xMh#`=0s>YfG z%S3EbH@(`eRXIbxyaIKtDE;e-(i`dBVtCPbqyX7Y`ez6{Xm}q zs|F2NG*J3rp+Q&^tGR%Gzlq<9k$1?Y1+<3Y|EMUb6b-{{8!L2?Dc_d1J3h zd6Fle63;-iPK-p;mqZ^;>$`box5Dwpk7g*kzLUPOCeN*~QtT+-5w{?&?!xhO7l?8dy_r8l%j>nyR|!)2Y|+7vBr`pZ8QpblI_x%ci{3YsPDx zM!eC{z?!Ybnqtx0C~OpR#LU_R&5Q?FAF3?+xsYK}r9(%(PE2@JMKX$DsVT~AR0kW{ zVc14E)X>@1I^MRd$x|w2PC2fA8v@*hx(5vo0#=4b4pkOg+gCaEUlkaVGhAU=>w3?~ zTGv%b?d@PqH18sRJU%6{kh!^=6mFMRpYNBu#%tCBxuPkiCyz5~g*`oFO(+xA#8?wS zBRbh;yd)ZM*7YIG5c*s+@|`<(Mu84rqT5BBfA;KIWSn@yp#-NNCpIM8JMX-6?%X*- zyORL_aW>k#+O2!`GCq53*Xcz0_lJwF-?)KUCpvVl#c!SXvdHr=QwA{sMC=!_S>!Ak z?%Q5dG5*g;LsLV$KL@Nyufn=P<`&Gf9+wZSi9;2|g-OYSI2Dgisg>a~@zcPX2G-P{ zhH79cN*C6^!+5v^63LL{t!qNB+_lu*~BLX}DD?zA~;^0;O)X>X~8xdoifONGsvWK(MI zw9PGW<-m~aP~@8MI=WZzqM=sHz}{%GychB9f>;E+{R!-@kWv-~Ro?<|JG^ScwpnJQ0Xu?#btas2gQH($jCg z`G&_het7?`rGcv_e-;^JTn}FxsarjiYifLASd(JpFU-yB*T-~?SLy9R18W*sQ-2z# zfi>0Yg7`;G-7F-MITI&mCM40%>vFAuHFYVaCZu6(z!O5H@T$w2`T6<$ROJOTF_x%O zs*Y>iDJ(2(hry;jSo6W_L5h$BTvENEddj+KefRqgEDCZT=B|_~26+uw6tT!o-k=U0 zxIs8aXQ8p6YNKg3l|D3Q)cm7*$eQoJ|2`UO6xpb@>jR%IuiCL=N4(BaaMNg!JJOhO z1|qx>4H)YV;$^~`7=(~NXozWMT3Q-&3E~ssErcc=Q8eyB|Ni&C)$RprA|K_u;PJ5O zMAy&KLD)zTaTD!1Dgx|2Y57=iGEYOONw&n){QT_Mn*3S&qZRW8HOZpA_U6wDYx35R zHzHw51D`6ja(m`}8d%f7n)=gd4XoMny1;Rh9-iSqM{9BeYqq@1pU8ocjL{L|6pyM{ zlaT}03KeTILL=$X;sH}PQ<2P`C)R`)kyjzR!UQlSDaE;;v(ic_m&l_X=Wp;`Sw5(6 zxnE9Lzwy>}oYPeW!nJ@)gRH4a_w;u3kTqX<UOPxD z0nM5R8#su#5n(4i4oY^Q6V@d9Ix2Oh6Bv+i5N4ud;*$DO{93u@`SZ+-VNF851FYaI z4n|mlqCMv;#G+rtnzU&K z3`SqZTEuv&Dq+xN7}gOV(Ilz@!nf?{8o9b1|8ke?s=muJh9wR2(7YrJb14qG~ zs4yQ|cnm_IJ>;55ADJh>k+fvgn1}#Ogj**8E_#q6Qc1P@ zimZv~4v<|{Sy@z&d*Q-)R>It{VFU0AW#V{_KNH&dz`#H-l!b*5ucE$3agP-v+Ih7M z8~ypLuqI^=Wn!#}e7CO8P@^xXp9u`CX<$wLX_*GrY3dT1EEZMV0q7qeyUYFrKzLvL=2Q)T@q()t{y*zuTHMEr(fo z?CR5hTi}X~rK}r-cntGFJcKAQG~a;=KUQhVu#M;MGLNj(p^4!GW?MWNXOy?0FbNXb zwQJX=yji=Z)ic(Vbnyy%A@iIo{^-$TcxQ93W(fSA71pGJAZaAN5NCaPaFeP}9}*i_ z)4-bg&<@3CFtBFJ>jI$zB7i{y29EuY@s$7fnX)m{H3Mt5yv)^epdGUqAc9Ozu#c%F zlAe!0{#d0kXS4%GakfyuQqyk`6Ro8f)mhW|);!j>tZ6Q@EVOmn>>9Fb#GTK z8vk+ZsSdG%QL3=+OgDlal0G;3_?&?@uk_?fgnm`)XL?Mo#^iWl5_4F>=7g+7yvKDtDs1(~x zNDBccB30z0L{nl>$}3mCy}31|V9C5*#=SYt_oaGB9e17~)})amhX$#6eI4IUZ<85V zQ|*Io^`8x{8>F{w;^{q@fi+uR_JHQlkYVAj{pbAV)3D!u;?UDfd!7c?)LuT#Im^Jn z2!Zm8iy2u(KlFFseHTMRF*`&-(2|bh2gz`)P+i-c2h#bPwq;EdnX=H@ZmnbAt-di; zaZ?sgiHVIF6+I^CP;kFeza*EaO1FY)-<8+)Tt1exZ=L7z9e!1rLz2B69!H51--7}H zXmliXonO5*6Y7^-Q`-AaBM)_m5scD=No$T2udcQ*YcgxbL`Mc>@AujwIL}I4l9s{x zkkBMnm6#yInlyD-6A?aKhz=d%q$@|_C`NiGjM0`YTbL}-hHu=s@%rnp*DY(2HSr2s z8R1S#agHt&EOeg zO=Pzb;o*@B!Y#}j>Bs99sebMoShLmPQY}LRYwF(e4XmkqS!qm3Z;y_ve}I34OD$_o zc=aPoNh3ko46LcKBwKnA@sFtW7FiP+Cy}T{xhAMY49>cNHnrc3^e8J;2B6v#xA1J+ zvZh34on_~?&&#hSAbov?vyXE^N`iBRn`4cu&-(sp_OaP^OV$rudHvG0^>Zq6Y?DCG zjKN8B181nI(IGk9-|9yNZ{fSQ;^q3qnlh=et=kJ3cP1Vdwx0enAuFH9Ytqa9=}&(O zORDU!_?}UU@IlP}v~_#VpFc-mgHI2viOV_Bi1>?a5?whmcrX+}K6Qkt12Q>+1^}H< zqj-s0Kv|JB@m2r)_<@~sKi#|7{=|{}7=t4CXNrY%6VWLKC-?@Dd3jlB*@TXjle)$Q z_cS&&C(q|wuGyQYvB?`h$vf3`T$IeAcJBJ@ZU)v=L&H}8X<$v=zYYUy>RwhFQ{osz z_fBU%);DnWZ###*)=|+2YRU|(sj(zmdJxGa)(i~lv}xp;R1EGBqR|zOrHFz!qT4;B zQi)kyY%IbU-O{%xRok*Ak0zBl&UJR1#K47L8yrv5F4{H>@~THr4UxG zSiWxBCxq3U9njmv)a?0YO{v`ccZugath+c<(_XUeQ}cvG zBiE6J2|3v+qmEiB)ql&;qel~0h*i-stgL^wp(DHtfY?8A+b^*ATq<;nuKE1ZwV5+! zAgZsfu7>tVfLs$DI#`Jqk{3s)iPzVE{_`J}FUQ&e{~lzVkSLcR>_y>j&EQ6#3s@5Ci&SV#y4qw!&V4n&!a>L<0awdE7z zUaG8g+-Y|mIyKW|YfGtI@t=_N4v9jCB%wp5&|#(U$Mj?J;q%(3AJDNRo0QDJnyvDg zYstjGnz}bg18eGDR%$6NC9)}h>OTE{J_-EyXRdB;uqGXxE}`HJtf^L#t@D$@hbfWw zFsvdLLIaK(5!Pg5sc3xCb-7J%+kP*OzhN|4_*X!-F6mpIrhQlwE*Uy(Xh=v1Zp)EN z7P(e=1f~si>gQ-`VK#Z{MTmHUnlmm+mf?Qp6 z^OSEl*2IK^_?lQuHr02VKnkOV_)Xk#?+tWKA)a0<7up?@wz-Hpx5+ z-3oxnCxA#2M|D);gvNv#>EAKCB%TqcR3#5+5IxjIgGsTX1mT0w>3&1!Fh2b?Fdy>xDF- zLyFL0sn8)!crEc8#qRwiADcy}Fk*|F= z`L$1i{_~lu`$N`LOQZ3pfi*Q2ODi12NrG4~$X#Gf^o)4pGQ?uvfJ#v}VT`nk`&2;F zC`|b4fU;J2ARV%3bJmo~Y$NS_j{1jI4WAP;r;k^k5g{Wa7LtLM{mZ@cm-@yP=M{;n z*oBb``w!}GA~CJYCE^>vn)oDMnD+CHz>mRX(GZ>r(GJU^mwPft&TrZfgsRyyvqn_N>WwHV;Hs37HjVTuqr zr_$B6ucTf8FU`*^HUDT>Jic5n6bnWx1*2r)g+if2sUV$SDC;{y^CfzJ)WDjp0+d=Z zF|elYAAx~2buTNml(uHlsNa4z<+YAJANN$rHPzB+{AplKjm6Rm2OS+985C*L4Aod_ z;_Co5A^t%jsAKM?Hr~G)e;wBB=p9#U>e%*Mo3o~~9P3Qy?e2bS0y3+!-7m<FcQ01fX z`86JFH`e4vbHa}2CJF_~LR`fD)WzpUhelai+Gu>Lx(6jn+c#$(M0IXdD(t)}bXoC0 zy17K?F|$%WHl=;Kg1XPsD*Fc3)Pjkva>~G(x;Infi;^{TxyBIO$QSR-pU5neByhotFTimvNrw6u!<`#!zWT4Mo=~e zd$j6JlElcR<`M;Us%wYd!Z&Jjxu#NPSMKPx$IEMbKl@52t5RF%UG75)hGu#uBG+6o zByU&64pt^Sed2VoO*A2piu+`ygv2A-L|&+RHIacRTh$T$+2JWidI#Lk+Il;EQ^L?N z(6iBJYd6-UL6)s3IdbCEH{X2o$tRyQ`d}~_PUQqJVy$y*G?6}{xkjjo020Y1i`n3W zjv$k%6gHY11RtST#Fi=(5o!wOA=nht5qK1fOg=_3!9Y`WcTuhh)RF@$Tk^f2n2(t= zVbkiB(E~qP71nj_OpAV=PDvxBd*hVvckIkZL|9FR3K$ndQdK^>@3b3h^4Yq(1TV|@ zJ~Iy16!MdV=y^LPjmt2%&>DYUD*4oRx>5Sw7nTdrM}&L#g>@H%S9669xx(woU&^Ot z%Op*9htj=pnvzo5Q!=WH#Vn~zsmoOc)_h`EYJ(roC2Pu2phRUWHk`FcC~3uU(7>9y ze6x+FF|g(n-|^F8P3j5vAmRd|VMGSQGK{Q)Lftf4e@`(%o3ke4(f~`l5=Z;>P6Tzg zU+3&`$a`pR$fWV(%lhOMj?0=oeKxEa9ubifw6ww{ztX=bB{a_0@d-B|eP&JS&d$nq zM|9Dp%*{cbzKxTnOxA9!X<}>LGxO`2d%i#Y)i?IGjcamgt4xz%N7xQSPs}d)i5?u; zJi$%HNJbbdI@kkU?x%Qx?yz9}bQX^`7M1W{_GfFCqL!qpDy$xmLy z&c@!?qo0}NabzDY&(=PyDVG?(JmZ{El3=<@IQ03K@o}+Q9*I+^sW!FYQZESR33W+ub=vce9XqH9mNF-HCK65Rj@FJzC>nNE=B#{!)*jnR7?E|%K}^J} ztO=xA%RppJgri)DdL7XwM9U0~%;$%PS640{=ch!n&*eZZPo-i_QDdOGk?uR~BiE$O z_VpYVHgW#t%ijhD`Xxv2TUB^#dRUU1i%%n(4XLDy|0JVg;eSem%`5(hgAds~^MQhfx}_=&auK~ERAf&Kj(>ChfMmcd^0 zQAxDH|6nfe=M2~klYjp6pHZW?!A)8-%l2SRiA*ug5|d4@{r!gK4eLM8Uwi@DD@J=C zeDDD!hHW4E)&W5S%%tXZwdbl=MNUSnZ`IFddZ&F@Q(^weixY1c#R~n`{jlfESHDD7Tf za16(w*H+vs*m3ly+jsEFa@i;tb7EGm$aG2VI$lW zwWf6XwPCV3`JEmk{?<*=*y@zxa~oK*4eO856!9CDg$&QIk&gJ^h7Z(O*6qQXJcCqb zAK?&qXpryp{^AqaC~TZ;2tnwC$4Njy0J>E4q}U$R53VVuhc&^S#@^=HINv_3DVLi4 zdfsWH3?ciNaQ4!*%9`E({O3Ql6c7vl0Ekm&EqF1iR(oqSXWz+huFal33-^D_G*Oyk z@rj%g^*S<6SQ7@Nd820+tvT!Fj5r@zHQxex;wQu@su)!}_=s4P9ejhJG4g~pu?;0O zsgI8jZbnowEGvFBurJ5CR%A`2prqq}9!>JdKC(alMxuT1rVZM|b5mrZ)7*YwvqjeM(`mo8tbOe-5| zAEeZ@E+4UYn&%hNvf^oztJ{Y)d31A`>B~_&3y*wDG=Ez;J|eb1efl)QLUKd&xNY0Ex^hkKi?CA^XTqa+ z1p$kYBAklyoy*Az*2L}y)?{LaP!oGi@Rabhgbc%c6K5bYLANe0@yfGt98M)cQ-jir z_16w^O{LN%a^}gRr9x%Vozthj>gzqcN008jGAqlA&!FY6lcSw=?#T`%Lj2C}QmYTH zEH5WaRd=FnrU@@Exuu+zC-)uOiUJ-jPtxeAZpJ1v=P$t~wb7I$FtBDD;N=rpKDVq{ zOPjSZm9a_FrKRbTMxR*|^PBL|CN)cqH>dPk6WhHcqRWzLrePyI9*mZ{yj**vG3O!Q z>M{NGPQwkXsrm|^qP=EqohxPYv*r)m9O_c!;W*YwX`_?RW_vUiP4{R&)_in#bD2ey z_1rxBlq&aS+lp(Z^$&wJQ=*e9hm=%%tXw^yWbS~O3c2D@@`wK$bm*$)t=ee_9G@sU zvz`;ijlFyKE+LAvuqLtDVLpV5DS0dZk}4Rb3tf`;UA*y~#LNWEHF8a05#2XJN`g;; zt*FsO*2HZH5k5iV>)sUB#61YXBqp9`&z@!NN`{T?+qV<7irE)gojrHfMrmEKtSmTW z=%B!Xgowvp6Yhj}RjkRwa5>qcQb)bcP{G-{chP;n9as|``sksV%QNom+H||3Y_pZ6 z4RDOL>x}6UHrCEs9+fe5T15Qsi-gyg3E{iGso3}ViqcY8Gwq~+1?Niz-z#Qk$!wjp zm08(OezVeQjL#>ZG2f zk)I_@F`Y5iqn_J>>f3q>J4iA^KQx*3TIbNebP;7njXs1yuGwf2wIG2+ri`?bl_-_V ztZmBeovYpa7X(_`HOI8y!AfRtrDL5o|AE_|H7#V4NbA}8_Dd@r3wN*FF(+^a__BV* z+ARCTLdVoX-}EIRG48gmYOiF_q1PX-bYe}J440Z)w{Efa4^RkGsue6h;XeXciqR{} z%U0H$y7-P&hOg4?3C1C~d)&FbsZZ^n!nKq^4YRXh^T zLog!gMb?CJ(6IBj?u~TZZwJ7Jq|-e9AqPo7#qWC{ms=@QZ%mT%E`t`bpfCDhi>xO_Ge8VG1PKM zg-5}b!K>G1RN7nF;i|l4{pM8bm=dRqBL9q((0EVVCt|sCiO8CibCcgt|1M}Z9lH4H z$QKc6?%ur%Wj7u<^@UQDDjw*_;b26fwVyeCy5m3I-m!ff@JC{pPgIDr5ap>;r{G0A z%89*?Q8Mio=OO&ML;<9#q=7;fxEI$J!l097v`C4SCjwEqM83#r?w*O8^X!sy?UR=~ zXO%c*M%&Nx?dv-xboA^wv$>WJ3a=w%nPPRLh(~+QfO4C9|3XI8IU6+PJ4i z-_MR05*MDIIx$CMICqMLNKdAoAur_IHOjglzV_md6JJzRR^GmSTT*Zn*8D}xPl_qI zvQdqA#rgW0O`FogwC$sC8d&owl4bo{Ki{k=Q!0aUR!b6=v7_o+TC&6E_TGE%z468y zzy9^FJ9Ow!zdUt0qD!FSraq*bgCuuukFv;)=MJylm>XCc*)?Z&*O_D8#mRP4b8FM| z48XdN!3NfBtS%VhoSdXSKJo`DJXLBA(^wgOX#p@IaEZ+sYC(iC4AS`Ure&phmGl)s zQ&DWij^#3&fOP}nGZ}yq2>XKTE<23(BxYmiWkbD4s#>6=qAI{Fo zzH$9})V8mFzFc@Y|CVx2uB`8{hU_(&d<_pZ{`|z-XHUM}4qu0XHFd#}c1!a-v!+yH zTeL}5eco)@9;r<$SZ&gEy!F;wbR(h_I1BCKEBWfHufF{9%P+j}g6>t;>a2Mmmx1CTz$J(IP>lGsq+?QWTcBh zg~S)6H8R5i7H7_wv47v*6UR^Bf77{h=gXHbL!VF@q{!?DJveUQNJgOz1eUdN*Hcre z4yI2mp00|WNp8#vFzXaU8qvtXO@aUcRme4u9Y3}%epQ-fY`S&a8n@#0Udzi|^AKhi zI;Q12q{P`R;ErNwB*p`D?&QWp0&ru;jzx5ebQ3qDX4FgjuqIDS7eOiU3hehZ{(FAP zjnXXP;GS=$Oh~UE&YjYupsXzAe;vIXH=tjn+#O$hWZS{dDRAs-BX@oGVvg`))=$bA zSu)GoAW#i$xtagixb6>YE8euTGc`6Q=2SxoTj98YHFbxQc1Za=v!+Diu>Xp5?WtZF z>m^R=CRNR7$_%VO``OP_mGXx_`~ec8C#1h(ql5bGZ+}aIM&+HY&YCF7GiI4o#rMdc z^?zTUJHEdx7^}_vxm{+BbDJ~4>zUT%8CX+WLzQ>z*!9ANPX)pB&Yj}@`!g~#8Hq86 zM;=LZ9+pB8afi{1;e_pRUS=|7td+9F+PK8*=#``2-~C=vqCl=`yV}7j%%%x)=La(C z;a+Ctle(^)*sXV4jvPGv(_&4f+%j%Lw3$UMIuPv$lmR8-lE>xw7RKSts1_+XdkENuf1X=~8 zKKke*LZ}`-d>A?C&_Tf!A;mQTt4dw6SGwoMPFuWTLFEM3F$JEfOROT1ZZ5E#3Fh*l zF|R|UikYeSgzR7&%6PzI>oaRo8rT(}=)lZ?s`>QOP6=@t8!A7KoS)_4;q$mg9__vT z_S-aRV%5JjX+wuJ;mvem=GqHKKEH%@>^EP3XL=x-9NEyt4aGb{(4U;} z-i|GA)>OZnk!pGLz&qh{KWjVOG_Ypf5Adn)KhLZwmDhx4}iFAgDKS=r4zy4K~Ky9bhS(8^99cWP%-@R~t=WE}7wQW^WWpt$?08dy_n5n5QV>`5m<=yB`T;APAF=FCwl6pS1UB#jL1u5x9r zmAu$;#-3T<-TjVe%!jWYmK9q0wcaX0<+dGfzGh)^wEH z=enkB^IfwfD5js2_ohvoq9UVW1}vQ8HBH4pYVU*50+t3)h}!XI@n`$WH6?!@w8AJ_ zcq3Cd|ATPu;^j@7H&<3xqK_uh5b{DCvj}F?fAEACBW}I8MCcTASQ?fRpRfdXB9!KY zSjVjg6A}0lV?;QWC4$hN&zUoax&TRW$M) zHy|K@eWrOR`kOb|pi%w(?|)YtGn)64w`-`$!NHUg!dKA&i&9NUh@UWFtdp}7Xs*_K zpwYuszxJS1@?mgvhf?96<@Z+|`Qpo~-{E%!Ybr~w6Aj>{q#u;ia^<114Y4K<6BuA} z_T*o_`24d&d*9r<;}1y*AGO_AVqneYHJdiNs$E%=R`tkAy2) zuX%9NN~X-Rk`^fnPZk53Y-MZ9WW~x_^UX=k@h5-p!;c094Q~9tElZ*HA!+#(wVZnC zrI*yaL$o{;qw5T-NqG**tK14xgA+2QEL{@6Wc8TkRi4W#1B&JZPPezTtA9D>HR#!! zC)TQ;+tWSLzN{(j?l$zLlyA+~-Z}K;r7tdCLj8^44MqeI!8qbyE?yj+wCsSp^cWwb``)^B z>!3k{xE6UNbck?tZPnU1=LHBaGh7mKQ?nhc9xglsx&fmJ;c}BNskc;g$P4EWh_k;dRB_9GR_S-D}nM8LI{k zusL}mdQ0{1D_6d;VEzXU^RU{bP5x zcfb1`b5v9`Xw~?rxr74eCpoB19b59!&i2yfk-e5L=o&Y%+krisH!crf71JYs?x%Cd zyZQIkFch}rUhRCXfi)W|aD~DpIoT^M%?im%T`jqm>!dRIAf;7_tx1LC;FUuUP_y<= z-%?-eayy6B&b498o46s>C+J`kFKTk~r^1?3EyrisCc>KO!An*xSsCg*G~F@|i@lP* znaQE?{?5MjUjx>}kIKNBb^An}`#VVwuOTK=Voshpd-&ji-0Vykh>`>f39F9Kv8br% z;K?ulk}0rEVz;Yt1-8X44^qe3YS?-p%+&{aY_e15ls+KJz7QQY%SvCn7)^Vkg#kO{~ zJez9vBC;k*eAMx(OPaL9(9))IO_XmlCv3?{yq_3(dVSR{(SZp`JnP6$nly=OB5oAV zpn7JC4$u^R6wqP(Zr1(|$%0w&?J+594xhh*NyCvt`;9ZdHCigXl5kBiBVYFTQdJ&L z?DX&NfB5j`vnT&^>c}5v&+OcG9&`rQtos2z_5EhErc`D;f9iqw@SmnkD0Z-Sd!jMb z{{f}N#!3ajLJ)cjg5AM`Zqd>8Q|b8;ND*^P2=bLzUU~iX*9rbgpFjbke?UdU=Sj!K zC&?6=c_5)ZS)T$7B_*}|^Pm6xH^2D}iggBM)@tFSMx}$1I_X=^oqb&;TbA}Ji~6iE z#{c@YE2p!dD%Afcm~#NsAL`=@9*o&(#txJ<+T?o^Q=*q zPv14`_Wj!`*0e2kLau35WGi=UY`A!Q^M`xSD{Jz4?sAu9Zkap%)};;K9{fcD}vjPKS8m z*9&hdCg#ifPt!Q*rG%WE%&o1=u_@oWbz5HEw38=47&E%3Zi^K+T5K8;7+CYE^JU!! zXdY`~5jJvIQc;SKv`~nhe|_A@)H?GZ4V5l4H&<3xDg{CQfHkcT9_&+8q_BI&L~ae) zYt}7K5i8-*KmYm9c&W2`1u_mMjkqV$p)rT#2z<#;OpnmL&<)ZbG6%-W6WJ#pc$1hx zENdq_X?0SsvPGZ8PV9dC$i5@n@>a)mES%qU&II?#Bi&m;qN}M&+vk{pH5)HyqQEe5 zX+pbE$dviD9_Vv+%vXWt(IZElSGm{f&~vTj)&|ymqS$?``c85g+s~=*f&~i!iybMO zw|K8wIiz52;LIn|J^I0C3~Msue01O${{N}4rbMCq*`&jSY6)>w1ozrcBRwf)K; zu6^&_*IV>P7gO>v0%!Wnns&DRQWjlZmL_B+o-Z%mX>VK0nuO8C)Ricq;#&~2f-+$r zp(gHAn74|@fyj@7=NVNBuNGakj4rwK%|+}u3-hwNlznehAiSFXtzuTb++z^D`Y`>Y z{}5h=&+T;m(py>S?~WZio)zkG((?87Z$hv2C@bCnH?XD}nmpZ~&0|f8#B}cD3f5E2 zTOuSx+@BG)c0d5q_lEs%Se<|{+3 z)$$z6HK_o(OkV1db8y&}$U*ZqR#Yz!$qRH1=w{Ia*wI)$2G*?K2{wr;BucAVi_8VW zFH?SSPF_EA{rQihRt;JsbchmumMnA+Un3vA2nQ5;NqWq#UAiWvC2cvj#qqFf-_w2r zHU^DI8VNye*|G&2O_m^HA1NjeKoHFxJ9ZEP4BeBe;xJDHOcCAD5wk#Lu5A*Qnwu6^ z4GMyp8r*uDo1FjZf^c8ROUhy4O7ZRZiw!8;hDIKFYJ(SRJ<-;3P2w3%8(&hHcRzRO z{-w#qgreth5XbiE;}sDREG74)49N1CTH>p)dtEE-16`4YIf{+v`%%6T>+$h$#*E&R*nMvc~+;88O-@Hi}Uc1mx z4UI#;549F+s`&=-Au~2Hfka2o4&qNfb2>l(6gwF#-Pvb34EjR|m9tDZ5tM?Ve5c}O zrIaRy^(dUzIev=l#`SA!m&cYa>Y6dLJL?ANXu+pT!4o(~VXJv;)#dW$r7^H(Hgj#*Q54=Qv>TkTPl-aoO_ zHFu*|S=x}q@%_d~8emO6X_m23bFgTs2CWjGvI(5q+I1<2&;5@XD}`&$3A@pS{e}OPl!-8D=MBRR@$; zc`O@l9ZVx3e-7eagY_pAg}+hr>a(p`^TAU|l}d9@FW0$q=ZOzFWy<8x;P9f%!=4`f z8INeXOH-Gc$USC9q0%xSFdkBnTa11e53! z@`<@JO&s9pXu5s#Th&!>%$xJiNs}hCP!m#QC|0bO<`q<tPCYxUujXs=nn44ZmHjWz4P z8Xq7VG|5KnHo7lV?Fd1cD${M!!4d$3pA2Ul89rjfoT=alQknCpUgIymJZ#@oDW7d?&q_eETa1SbV%_(wD!%&a4 z3YQL!UlbH>snG1<*d)28sl>FKX*XP_%_aI6Bk8HICfH`;*#B?oUv^x&<2O^T7=;V} zyY$}BoNWZw`FZ|blc;j}$T?;v#wlqjLpBa|-s=^AUVHmx9nBe15Ik__ z;E`L#%!`_bj$376!b~03#JCeTBfcxD36Up$Mc6aZ;UbSLE-GHVc;&AC>no;|m`Sx& z2fgZen~|7oX4xhLDFaljiHAGpP;}Wy3LF1)HKk}5)~vlT(T?z%5+5NlA}cfQPIkim zxzm>i_>Hr$^e_qC)-CkVr^8R%4qxHmJ%SpkO)0hb*O=@MMZ!C!_g5UbSXQ+W(}l!M z7hX=k-yuoJ*mhbzxj-h-Fhm#|%eQU**X6JN5*OI$nX&dH}&+?OQr!5v1&;4%V*kl(cAMFi>$rAR*C>gXT)7!IE*Mg=+S zE_BNENY(QWj0#P~^`aoo&WJMoKp0zxyo7hvX zl#bc;mx(uC4LR{Enel@@r|u)R<-UP68!z1^vu2znc9{cL2Yr3xYmqhY-@lJ;F)A;r zHtbP}wPIY8S9E=jS(6GFtPCu6PS3VmBCZ*;&U1O%(8P#=bI`48tRCH26GkbST5uxi z(7CM7gZ$b&Xq3jM)qe0Pu_g~Cb#!qFk6ifsk(-Q`2yYkM9+6%3!GaC{T(nj?AXKKb zWc8q`>MH-r0mw3k9vYrolQ;Xzd6U1JcIfJ1+bxdn`@JUa4~r{{A2c$Ej#7L_%#!d8 zLXb%S5&AxcA~w`UB5UHne(v14@QCp1r>-oFXnfhHfriEyKCH>>2TqCHN%u&Q8F5MKkfDQ@#W=8HIvz;KLB&fzRcvz5^NW2Vy?ft(|9x~+v{yx6>lL=jP|e~xEsyeiv!=PsVw}y$44e1@$E9n%OSTVK z8#{PWKL>A-FzOczpAd%Fd`tCD)jfg=KFn|IH_6AoOgMBk_4Lf)Q|+wVP|Q>J*1GRM zJ=T=b;iAwn_a5 z*RNk^X5VNLQM;+0v3q9J3=Iet>zdp|)`VZ_rW?&cV}kZ!O&*npWDMXLx_0f7z4Y_K z6rnKX{@fV_Bgf`;9=xOTup^yAj&%+_-ett+mcv&&`8_Z{m0J9Dbh%NA@QbATn@-)H zki6#G%a^Rn1caJKNw;SeZtm?pUgJxN2Te^S3l~Nra3U%vL!-BM-^hi-+}+F?`%tuG zsqK`3HQN&<8@*RcSku(ZI6wIYI`p{twF)z;YU^FGjbVLeY!J1z4?R8y5&r$} zwLy*uU4$U>%{Sjfib?zwSn*SioU)+=}|*A<_>nw4eAf`#VhyaKasEY=dJP!}UESDz%Njb>I zGw=XlsExHvU}BfaKfE$z=WnIvbgNI+GQUG6Sj{C`c zIB&_|7;8&xQ=a?`>J$y%CoX;}aax@-PwZOArR!&`I2X5{f8Q=WbuI7fMdKej0zZ-N zss4D1tVzd$g=HTvFZ4PV4z5zU615HramvHO)FL(}wy3b^?AfzU8Lox9i?;0EvSa%W zL}lhOGpEh2Zil>5Po#$xPM#1kQN^0{p}TkQ#!L`_2c4|gk-?gLK@kfhW?F`A>QjC^ zX+Kgo9gDoLi}%fMt|uiwjCn-8;}=h-j9)zB2OTp}OV&QD$wiE%4MR#G%7h--0Nm}!9AftPO-vtGZkOKvJW#uYDAZ4Y0bm7e3^RnM3VxU?fw8~Ed zYc|S%?U|q@tZCfaxH#=bUJ|VN)1-0Pf&C}ecF7HIB5SgagZPa%oKR2C@JWI`L}JHD zh@OP6mAM%ph&WT+kW-2pcGT-2CNPQ|lrWqRRO_$)>Q}#FRUs-D?&MlZgN=>_f&~81 z2*H1Z%V3VpM)yd+3V`w#1cU`qonurzeOkoSsdJ}Ho;hq-2x;grG3fN^>$x@4dR3fp z&5GIQ_T}v;3fNp2Qj#$%F)GaM)G2e~Ox?NTk&$8fAjq*=;ZheOx$;Qt@FEyBLBl)) zf&(4^MDHOFW{d z_Y1SLY{Yj;7uGbDn3YY=zm#$U(7X_T@Iu1DOUcKJCucR}Z=|8(+vU(xWKG@=BO!vT z(>IClN^eEPqWSaYTTio@dTvJE&isf)5wm8_qLTt*upI;<$Bi9lGr`XHO2E+{j}4g; z95}Fc;x1lJZ%c?x;%L&ZsS1ynbH&BQBZiL1v0ajBo474;BhlSecT^?QxPvtn!QnTo z-(dV;VLIv^Q7e({u!BS>tMQUX54I0$Vhom+mfE9dZCEbgo0w1zc5Y4%o-&1H=YYzt zBTj!d>dc_1W4%V7>@xhcB6PJ^NSs_=E2*`x_wJB8;(I92R|>Bb zT=h*V|J1=>`wJqvATmHfK|#O1y|+}qwXy2WxafDyEex#Lp49hr?$r|3gt$txZxk*S z5*OT=G&X0*pqY5EX=!Po6>?2hspXT@l1y(;Y4<~a`Q?|9W&Y_;fBM_s{>C!AY{WKV zX2o0c7obE=$wuc%uSl0TYSbutynuiJG&!OQTkXZwC=fsOp#yTF=X@7E=i1yU2S*H> z=i%-F^U;wG8tA_{um6sc!Q0EGpW2zZcV+OlTyA&6fAn6nXHi5rD@Hl$*m7ApN&-O0dZs9*f%bZs2?R5NUV9mx0 zstK%##35LLv8G9(dG(2nr!SttQWMt13B5Mj*4&B2JgT+VTwrNnO^xM_EK`d(Q*{ZS z4YNQM;;4?--BHLDn`c!3npe}$-#PZf`S=6u$A&J{CI7nlwDtbeWliSy=zZ$yqbXaj z;e9;zc>5mrFaNT#WXm#ZD{H!DG(;@$&HR{IGBj!4!~FEA)2ul}MWCY*O9@E;a!|DA zDnl-g?Ao=9{x>lxG0{3Q+ct4~T($1w6DhM}v-V8hoa2##0GxKCX73E%b_cfMxt`T5Ile>U-&$=EY}MrS@S zPL}rY7>y3SL#kk1b|a;7-~3fKjpBt4ON94gcRS8mJ}A&n%PUiN)aJEo*V59`y*$k} zZ+L5M#T!uz|IIplTCQsLDT7?|=|E)zcWnu4@|_lE-XQEz%-kQRj9(V$U%SASmWIKX z$Q+eVn+vrh)7w+pS+juxHe!)zwRlPCwJi^EnDAtJhLgp_=S030eEjHl@~ zS>A$(Zzww0MvfdYJuD|L~#B^J+u*cd2(>1Z( z3g>s9965+pnh>C=+qVgqJ#PFs zhn3E-rgEN@Rt1HINBVD&YifAy1`hFE0Gdq2*l8e%jM$td6Eoq%ZhsBio@%vGtAqu4Lj+u<2c@qJW?@K~$tzXd)I+Y#=C#h$2k^6%@e&Di-Y6QL)!!uj~D8*Kxbuv`w?T zchAQU&YCrA*4od^JoB5UO=!N~$f-sxM(tn_G6z}I+P&p_#c01 zv`}xu%G7=pDg8F5^edRzV}7L6Yh9+kAoPE6E@(eN==1ccO=YD`bTlo)ijlC2xRxD0 zc4f+lD+WqS%ogaXfi+*%BvAi?{1MyFdi(jkD(s}34r{hf$Tne3taM|S#KrB2uRgis z>h-HfjvP62<_wWFS(ven8$Qp&Y=fo3*QE8gzFtFzHPz|CL;`C9eOj7PGU>938BYuE z?wDThJo)+Bkb^A^Tr{3UO%)fQ4i6#Yf2ux8CLGEd6zsqS@z1Pe=iN4GI zWeTNKjOZQ+=kPn>taEr7W}TZil`lS*G32a=*LEML5NF^N<&1Oo0Gx{h^JQC)a}2sR z43+3914DzG$F36sNlOJfFGZG_YMMms_j#r>R9F)Z%Ll{IRUh1@43A@g)p*%xM@ch3 z#4uq}-KsPpC+*RUxKeMAsl?AqOIxH+D5FpGa?8LgsHgt{yMUCUvg)WMhraYb^0ohw z!J`*B`YSyxIHvMgaO-saF0V(p+xQU0u#w|8C`!1>m2+_q(vNNOqwzU2gsr?FP zd_8ll%?L@Va?WQs6E!CQ%8&$p*MGsYlVCD(H%Q>?hsFi25Yhg z5M9}On$M7R!%XoFYH8Qe#3$*nrUp@%SNQotaE)^DQs2m+XyVHISr_lDzveo`73Y#O z>yMtDxBo)Q;VAEjCLeiQg>M~e!gdKE1Oii0z}nJ!?$P<jMCDp7oo72!#F`*lEZNz?H-qyUx1m|m)mW1=S0cxj6f70;mfTB- z&Gqq$ik>oeMaGWd?m-f%>}#K@o@37R2|nsOB|CF<+0udoUxYOD9(Ue3WSMi&LgXVd ziTt)foG6$d4t5-KS=GiQGdU|m>B_IHMO{xLM5k5gK zO%im{wGL}G2UgxnIgqH`9ow_l7i*S3-I(;GXGk>Hta^N4?~}0cDduK28q|po*591ccSCaT%B*2?lE*3T ze5)r2Jp>`-;X|-yAFyUkjZ;>ZL{CrSK`wb0dwxQKAmaq-ityLV%L^vVr}D$8&=S^^ zN|DGSAb=E&8}CSo4r{hfJ?*ik^0383WpGW;&ECbeCC5%4;}Ab2J$LWkMN>(LO}*s? zCTq;i;>~qB^w#O#T8(APmNB23tYu?rRg3U1Q(tuH^*pe%Qw=>$|+K4}?3G)SLVon6tvzOb>7(MOg&6{v%Eh!^bvyod>MsL_z zz5}r+pUR;`?A`IEY_98DZM>_oCLdsCYBevRDlelUY}~Byu<6;U&$1Q>N%2*~-Ny`a zjO`nK+ak7Mao&!soRY&w>mnAPRZOaP3QjXN`Az^iz|O1tf``~>a$L~&Rtw$N34cr9 zFN@A392It8EF_J8BkMxK)Sl^SaU~@s@GH&@q8E@%F!DQn%}~8vhc({{rflw|9l@G^ z{>vXq^PUivYvIf%;o~z#j);;pb7q^^`^-e{OojF;!n=)XKA+<)crK`(@wCO2;$eemFGu%;l$ zj~#PbzFg9p=#g%MCj+L@v!jVZKtQkIdydUL`XxAF<5!C%e3g$+C!1{Qux4}AepRkL z)^r%`5V}1KtU0b?!p4IePngSL_lU7>tmovwI znVVyc9})r#;%m|i1g1_+O{G)RD|cdEhc&eaC2yc+N3iB6fA~qss^|Cy%}=-!8M?&H zMH%?4<%H?so-=2LBqYeY@@%S>zoFfC*@?BFL)X5u+JQAEjjup~PR#s{QX?vt`}7 zbt}ekuKH7+US8o-r%Ozl4$j!LGEx2crt&(h*%Vb?FVh}tvK#2>-qX0i zG-`YF=0g?xPQvqRi;IhQ?%auc1eP35#jdgflbOY{+A3_&Va?b3L<(ZSrldKn$*LUW z7I1gfmFfl6rDdi0`T1+tu02Y`7U5hI4>AZBqqBcgz-d#Ri`a`Vs=e1 zR$FCOhw3{@RMQVk9ZbL6hj&xr+c2h)+kj40T=OzjCEz+Lqu_0Bbu4 z4o|##^{NVMVuyrHQajoaA9*ua6M@1o*Ql(8PnONca8oGA5N1!?91*snm&ckufxEZY z9F)pU=FgwEGJk{Bq@#q`?CZD9B{)@PY^wZ7iBUh7fPY_gM`@|KS@?A2VQEkdg%FcC z04xj|zg;FbtlIMH&1Jvbx7%*{@>R%ekSo(W*wTsNNHywr)};<>Y7a`@K+TR|&98g? zWZlZ=*(pL&+~v@a6!#$^tqc-FL*wLR^tl5bJaEp;ly{k(Mk}Auf!i=EfEV0=&D(E& z^P3Mp{O|`q_yL;n4_+jY=u3C=mc*aUVNIqgiLVngFaIj1@ce{Z3#UIX$$y03@SQt% zn!}nXjt~JvPnlx7X%ohp-v~lQeLar=G?8l>A3Nr}Vui82eftOi4_UEd1*{$&5uyZ4 zk}&Z^;f?}|$e?_r(X_PwB}WY6Ql*VTUk#QTk2ZS2rrP*!9oB4}U|M2L)@HnJ2MyGh zMHw&Im9cf_wq3h+;zpNCP8A)v{ zZLVCoQc+O>09BC`I>Vbp2Xicsi8{o<-n@A;&?jYK3T9iP@C>Cb&TwFzb?epvcf{Ut zb$87fwv?02Hw6?Ko4&StR<+-9#?RmX_PLwm%!5z{5yuW2(B>F)%k7AF-wf6?HL;yB z<;;t7DTUmO8x;jYQbN9cVBXgOhX;6dI;{By5Ty36=m^&Qs>ctDvXodeBjrM9NV>CQYoWZYt>tIV5QV4T z!-tLq1?C(8@%or-_n>++4>E(0=b<5ilMo{l8+3Na>}hf626ExYgG~`G0ipt$zwkGh z8;SyKH$VF5Be3Z&e(?*s2K+?4bnqF0Drt?xHkgEC(958ep`yhkjRWbz!y^{Yxwl~Y z^Hoc4J%0QI&dIx){K#5(07EhUZwt?f~fwl@sv-u%-r8pk8x2bZ(6uGP+^W;rn?vGUj9ek~m)M?$vM4FF0^1^+;JvfnlS) znnvSq^7384n*3h4Px8EE+vWBXw@t=_M|3t<(E;3voDz?9oK6_eoOq5<8TN`fCf+KX za?a!IlBu(P{d!n)d3iazDG}wk4RKo?zP#FL!=BV?LL#f$)G{NHO2$|Ql)2{@4_>Kn zV#rE>0v(L2<>QU3y&0^jP*^3!og#=#-Vz}pW=%-o98Om5>okWb?mdG~+XSR9%qRp7 z)*U}>8-1?#_{-9eYL_wT#-?gVcd~dJ4w4$_tFPi%&B94E;3G!M<)xoJy8rJz|K0C?$L1L!B8)l_VNFt}t|5*x$-bhcz2dX+~B1 z`a|`UmROS=lH5ejp+Q8@#A>s$Zc9yV4MzHCZ!uy+-ZQm0UYcvH$Tw32C~(w;Q>$Ll zYDR}OHAn=1Y|b|bC-j^NOEA{Fyrgbk6vCs~Q19CLD`)4{;u};lImb})DpZ6Qun3C%0l}$Dy-TQ3Gu(+jM9S=f#v0g*(wSz%;lGAwnZ~I4(q> z9C$R*-3jqS*MLWrf>Nmz);uOCxN2$Twu)^UAW7AJ3mII|<4q?c7F}k(0IUhG2X3OB zZ{d-aZ@n3;i4J{SU`A<5H+tSd zp(RE}TFI(8->)%q)|GT31v-E=4JL&augMfR4?TWz)#_Ch9I|a~Y{^VfA7iibL}84N zmpy$d!Eo^MW!IdXwi*t;YU-`#5J@7*gF(U8U@5HNBB122h&#!EGC`K?pGgn|;7AGe zd6vn@SwymN+l!Z)J#iejAfcuGu=lw85GP*xAzzG8|;Q+7#cQDVw9W9Nto1wRy*m z9SEy2Vq;03Rgomgl^f@nN<1X21PI<+fA`B8I;{DU65svLv}ZPI6pEJyMuyAArf^Wu z$+>&{T}GiFVU(j6KAExaZf3*s@bo61dRv9>3f44`$}>$Y%B+2AeGgtb6uESwME@0+ zfq?KzkRl%I*dG%rT(whTD@8~zTv9Oo0Rv&F3GZfS4-AFdLRBtNDq#bPD6}0!25ZKd zOeDl+y6GHaOA}tfm%RH}lV7SZHXl7|#@NvbHa0I01(BQRdn`8$+F|P%wIC&pLo7K@ zGGoc2(8-B%%<#3_0ukj@!;oL3DsDiOBU(oGnVFg8>0!C8(r4UQ+h#&Fw4*E3A2IA< zF!SGgU+MPcB}2(ey}$bXMtL39>_k}EWK$i$ngds*-$4cs){L37A#KX)BO}&T4bF}m z8g3>U5Nq`2z=87h>t!#Fv9UOCz$G`=xGig$Mq_W~5+w|Ai+=v|pa1r^zvV1SRYC*y ziCX&0U;cv9oG%ph%WQN317ad%5{M0Wiu@8+8#Z-hP(ZWgX1|BI5duyU@=ll`_U;(} zV5bxjF=U%=F%a7*y(()4ueh6ZavFcXBrqemMa zKI|>}q1V-cHD$`7kDiXr(vW6dlzt;SV#n#<_0 zW^dT;wdk7 zB|GI0A3ofFbid42#XAu+L(80oVP!2!;nKR0FJ4x`Z1dZTyPxKGgLN1>iW z(4tWj?G5sKUdCpCg`}`>S=E+L>JI*L>EeGj6(ULF={8>)Nz8xUyS&@y`?~$JR?pB; zo9A^{vq^y1iG}UOni84JaCQ3K+$F-IL}5~B)*hdY=Uq1+8oXwSN8)G)pI2N!)y+0G z@(UNFoSOLTnZx$&O2fgV{YRTpIlAt^@61aAHxwS-HMco3yVzfjHq#_k`X5<((E zJ;2sbjJNN8D)kp*~Ox%!eVt%o{ zbiG^R5Q_|JsezQ?h@P?8S2nt#!2S zMvO|Fn$%1G-8$&vUBQ|!=egxBbAhgx+*1lu(V?SW2V5df2IW)VWr%BPN-A>Yc3ZPxUz;>D>3l;2-Az~WF}y-! zJZOLUCb8!0UnSAE@*$A>z%jKBBd5p4O@%qs9ITg(+xO+jy?sV)b_$$lY}$&n0D7X2 zYm7BnI8oKH6F{77Wo5mwEWKjmFV_|QYG%S`YG;eQ{y81EA}Ia;->; z+?XitSPkC4@4RlEWvKFO)eelNx9}*kfAAu5N_6FDz{!x%mVoxyFp~g(BL1Tq2^H@gT|6)MUi)DI85XJNDtWific5 zPnziZ=`jjYEc z)@h{crnwd-Yt79vEv18$5mvu@toD_O4&5Yf8cn_3%XbHB!shbx^6QS&Elyu-p#LghIA)i`%Y@(}q43}2 zG%Q9t3>V4Vw3umz`BJM&Tk{<@qZawD+qK@x(h7?ucrIuB^d8iAoomjOCH2TVfr;%V zHO#T0p#dz#$K>Ybs{6>dwavGNHI4dwK5`G?jr#cQbQ(P`CO#2a@3Oq@{l*;WG5T14 z|0+lSWHY-~>`>_=7B-|QWK=(#JR)RdDy(hXWfd$bS#s>quXk?$Xu-UH8gz+qve7q^ z$Zma~|FREfW;VhisxMQ^=&)v|gG-GD+lMus2D=rn5b_oaDKmu7kTl#PPn2|T7q*};IfGhnzw22T2%<^|h&wrXpdpEsf`q#d zC!c-!<(JS?lyUM`gVd@^PP=8l&DNL4(X)}jh|ED6VZRLkMcaUJ9mi8E{RU>wo|`;> zOWE_XqPu5KfBX3HW8|7dCF3p$+Zd&j(Tjvc6L^5h`_zdOM_#>Z2e^FpY{Q8YSucPl zKIx|W_d6_HC>b=UeS~f|)`Y_#O#o{GvT3AEZ`WbX*2&c>)}#pxgq7T1A;mEWd(FV| zpv^}rR&UD9TAqcYE?Cpv*xr1N6}OBRnn;zdr4o9JjgB*c&g>n1%bM43u!Jp!iMduD5{+Nr zy&;3g$X)zS1}E?ik7h6>Tt+MeYu>nV1Ny^}VU*z$!;P~{i79JZYT-3^#QNRqZLDk% z53t2&U%tpId97X6CXcmw`FZTG8vlGPO8~F;@81V#;xUS%;625fUj-iR6>`pC?7<-+ zDY$QCWqNf zoRH}`Jj~a0*j0AP)itSlqGHWS$}- zg@qy$VW}E9YUJFR+c8u~NqlhX_$|2TfddE7QE*oy(I)5<)sTyomC^e3_y+x55cXZV zH0#P0r4IesGt2GU9kn<&=(R3WyRa~`T$#O6OYEWcoRr@}q;Rk%ORBxKO*6h@>QB7N z>#*i4S~Qny6>DP027gyRE|{>3sI;^QAnObTNYchXA1IC?Qa`5be{ihb~-G5*|(;mmH%O@|MnY-`&f`cn2 zbgFNVL~3DglCmcx_sZn9d*&&B&%1*)L6bN;qU;9P@nKLB(21`Jzz!Qtk@gg#t7ZFy zo``Ka?A2Ie?3C^5c+}Ok-aQ~Iuw+|_80Ldm#4=;qnwZrYCiB(@<;BLt@T}NKJO0A5 zA3b{1vrkV9t=?CxDKXIVSl(~q1B)qVvewqr9yqdV*Up0c{P5^R&-h(-exd@sbrQib zpa67Gk&zDa_YWB9GhkcApAx2hgAN^uG974dumA4Y(dn?J3ZQgRrhQn`L}8jf`@)OU zDeFi3&q6ilZ94SPh}ttQWmOK@$)0h;tlU~3l&Z1#o@B(W8k~y79Tho@j-4}w*qZ^xFuK8=sOUbEx!#fz8lBEXuiG0k`!tjTtLl#h?>`gJ{y3wrY8`1R{I9zHaA^r-)n zC-&>t+r-B!PYi2&GPXAIT=D0yn$IN$ZSGjGCZJ_~$+{S+O^UuwVQS z)wMCWg)FNdPo#);H1+OaP3$R&tXWi4gya&YiE|I8NcUwLket)Cq*6w4zs1-PT4*#p0F@|Im--E4czv9gCeFSW|&%P zpt25YcFK@zp^^4s zP2`i-md+zRXE-@}iNnBNZhdLQwhL~RJM6P(d&GKJ4>6J}2HANV*!#SJaq?zf0TWVS0h+l`7mOu41?c z&6+mN`NWC-f`I!u=LQ`*dem*g1n;~&lOaReO{kIie+|3+e*)|NQ>v$Wp|*_ymjg!! zZd>4nd_HHDi2kzELz2C zjeP@A$HUVi&2z0RH`>abzSBkW2pANJed}K!mFlpj29;n9I9D~snlJ+Vlp}_YyRh`+ zipg25<*)!OJ3&#w!D9kE0{iQCG~TNEmncl-$vXqmj*eM!a;&kUO)7=&nt4aCCfto} zG+{Uh6;4D2tUE9~5hR)x-mzk3Q`@W|^tA>kAH|{;^Hu8TqVdSZ9j&AO-kdp^#No{<>`4 zuVzg9%kW_)YYL}m>3)ue-O*C!Xw{yf+r-B;U!=pDEx^Q1xz#?bDfSpx=erCCVfc>u zHe~;KxAI+fE0JsZ+YJ3fSpN4G-uqe76{+t`sVFIFzN6oZJ4lFFQjub^kw#$2-j#U- zi(@qh{IRUSC6HxO4crODM28Ogqykb*Kqo*G1j%Nbr5|<&x1=0jA~+$In($8q93V@0 zAQ@I4bV0~9vE~}-Jw9c|)42(^cJKK1^r>?jH*RL@&VhmxoJU7lju~M-6mu*H$j1;@ z4tP3m&K!%48Q@7pTud2Rm0c%2id?45!sVd7R8KWpSU1PE3$d(QJHl!>{ z!M>qtd)0{bKJq*hMV^_=@s;$8Z4g6~MpTbCm(gKO4a%_Mg5VotO}GH8!9Zf@<~YRC z#FDU%h@3ZU*l_CDi5;`IR4v%vTVEMqyt9(gAj#w$kMz0_{%IE;QS;632-akpaO@-& zgqVB)&@ncG&+@GdPE6F1Q;OdW!HMC8Toc3z>lBABTYsKK|IV|erKMOYQ4O^_9Gp0^ z$#avB)*Wr!Q<{Wm{#svZFyC|rAvTwo&9k$yLw64^ZoYaecitM-lo<4LUHEm->0wik zO`frQdDe>T>=iL-yZY4X2!>*X!FB>-Gn#>>+ zU!Q*ZDZw=fxB2nMALB5DUFIME_(zsqj=LC-eH^G|7JBh-XI_qI#^u40It2MjoQWq2jc#q%YpLKH5*29y^ zE28q_$41-Q+43$tAc>Qhx^Us3)vLjpUkgI{{rjux>WCvhJt3j}iXDkGpybb!9{t33 z$8V&qbBNcv*3gIpBjQ;pN=w$Qmbzr=5B4@RX&t+~Y(pp4d`XGs|7TP&P{d_bqZWt< z+2pHr6jSNZa4*&nHmuyXYRLXjM)?d)-+yis*W|0*rO0+9BVY&_)>>8 zHE2S2Am4gL`4_h-M7?ONFb*F!_S&*Dm)17mk^*KrdFteWwCZbV$M0mG`$PY~Yc$ot zOCV-KzMoHo%S+Kf+uD04uqIZgW){TY*t&HqKoj#~1exN`V1$FzMLI)Xw%UwmcrN&c zoww*(&W@i$5qTV@jBAmIHL-9I(dy~br)d`M**3bU)kdy4K@rTkLHu)aaD;L5RciO! zmVH}TlQ4!~`0oEEu&$?H&4Ohc5~d|=*;45eR`5;G$!|uV7#y_NDR3_CJ=**jMD;Kw zu_j^|BrA9ZC(Y`8?dl&6@Bg@R)33`)er;v-%Am8kHsvzu=2-n*)B0CU?c-uCjTB6(<&}(dR=6J>qA3pwr-22^^3jdjSOBR+R zwPxF?J-}$^Xn6yV!3WUHAdEy*1VH5c9zY=xF@u7F!~@NNhCoRXL5hW<{1Uh+>fhP# zp*;s^VkW1=g{i4{?^uR9AThX5Ic>o;oF>z&=ope~UbyBr}HCbF4m#@K4OQ|Bu z)V5;qteuHj8?t83n2A;qtU0%49$3?2y)w9_DzO-2(8+3{DrqjG!{RhH;+Skt8$C^e` zGe2{m0$;#lMMVYMDwr?uMI6=qCHjVFj?6@W`HCV>q?@2g?3Vb|hzF2p@;Kr~MmzFm z#0D^2jO#Ih!YUD8mX1gwd4-wGWSv9K*}M~j9S0*$XdkJ?r`{UYlp6N%ukAJVyk5wm z>b)l?L`DPRCrr&yOno$9(mmJE9GjsNw2^D_D_DWB2BT}31*~>peG1Wz^y)2JF!!?^ zTR$o-`86uRR;h&NP|2i0r4xHqMD?zlYj|qDfwfudPqo@?lNEGWQymv}MZV)$lM#jf zH)q+(=hM#K8n&a>AwO|=bg+ZhPgmdVzD)RG;yrozO6kz>{|B%p`tc7w_&^oXNImnt zmx-WAB>dBnbYkI+fD>*e`d9E5hEEcum8i%?VJA{j7#-Xa(IyE_#Vt%ZQ4N7dL82>G zWF6Rhf8Wlh)s+t`)(fS14;vO7y|Livwd8|mrdLoN`G6g< z3P*L0-W>H1;!KVX;u2v1b%6K;kp*kQi#e>49Xv1;d?{j0B5Sgt(nfgS0>$`@hza2j zA3St%bwL!-Ld{lhy)CTC5tRC_GY3RJwTL{h`(XW$Aw$9Cix;PD-*ZCGafZ3Qm-gXf zkjEe_fus-|l&=zU05`{u>G$aV$A|X)vHyV9*AiBX%n7nf=|;6P`c+2t**K|}w|)C# zptekT9oFm$e5u}Mr(jJ?fj1VHK8dWmJap@B&jMl|`IuP#XWrfJnZoyHKaxjeO9K~a zd6;U;2sbE)IB%yN`qVZ3D*rL-AWI+6&1QKniCN3?&jv~ zWVz$OvF%$c0{s0s0dvfS3q;NbZsH;2Qc*E;^XB%eb=pPuUQ(kXWA~-{_OW{A@|Re% ztu5)WX6qDd4r{V1gA_HN=fu!LWr)qGTjO)9@|G1Z%Uhi{eq)$Yfu3h7bJCPK6UvKa zgQZlno!75*So3vyC@~Q8S(LYlx4@cAau#4*l~aO}ZfBgil2;!&DUwN!d)8o2hwH@` za^hAR7^w-CH5zU2<$H`Z4W-62mB*ml>~VPT_(9dNYEfDSTU4rVUU)rY4-T3{m&7fs zFo`oq2JQ*sf#Cdh5@)euuZeLI;ib@_(=Y6OU7VbwEhgSPaeYqmoQ`8nR9XZ-=N0cO z)_gI#4DEu3PApxOlZ&(?zp%(Da_lX}^hwZe#A{D}7a(P2$3(M-Fmbr5SRWQr*BkVQ!g z&YeE{eD>KJL#xlF?w;)(7i{JF)10f_vxR?4ek`A`Lh3VJOX+)iM#&p|26ADb$w}zH zdyx=cs!KP?t9JyKMBn_wAO65X%b{FUmz@;@qEF|r=?@-&$?_pGPtN=*})rm^;N}{27L#7>NEFyv!DEW z)^mAMopi9bOllG8jBn&q9Ya)d$9F z`L6Q~VnG$pK!2^XO{DUbCW$H}>V=-o$mt4>sl1VlN`HXYg`Cq{UQ)e)jiXobmNiNXgAhmybL#SJ?T=q-!}hf z;qK?pp5I<`=I*fFmlqva8nM9M@aqq=E_Tm)_KT=1^6@Jq{`3DIz?yJuf@N~PIl`F$Yf^=i^E`DOyoh66tc|&bW)i)h%Y$;BmRLlRO? zRr2x*Se9}jJNiUrthX${GeB(ZPA>z%Am|xH>Q|O*1bW0Mt$3GktwpME8 zhGaz&5HRq{rG7h4yQHp;N|@N>9g_@4RfU&TxnHlCo|DpPG|?r)T+s*4r^-A zS;U%jjS6d`K*yI*tmbL&esR%}$1ATapOB7w(Ej9YD9~>#K51kAU6>w?);f6k9%D_; zAFz_U?Hd;JZ0f@&4`Zgpi1Y+m5)T|TI@}Y^g2w?JtA_OFqrzx1x@Gh&17D8e4S_X*&LV3LR>$O!n0oC5Yj383?{ z&p!L{kAJKo*KDru{`R-O@e~YDJwY7+8HtujVwfZpGN1+6m#3%4__1;M*_YR5oj$v8 z|K*f}SBj2++u zm|||LJZ|GnCg3Lj%%zAmby%~R;m}2LO@x}PyNt8O{VgA_bVWBUvXGUU=I+QHlI5;c zpy!%OzdO!K^9j645wmNSzJ9I4ny=GC8H)!Pq>^ib_HY2i;#;gXen>D!1f83IpkQ)V zVp8Iv1yz`DQu3+Z7aHw!%H?~FHKD*Tr(ty$ju9UU<57_yke7`(I1*ngHt`|=g(Gto zqF4dIhw_LLbiw?5jNc2ct+Bu0y!J#902IdU?crVQos(;kQ8+iZwzd|fQU^&5z<^cvUDYy5?!1=Z8%#(hu6&D0MesJj}l&h=q;G^bg-M6dt8S#n^~Ybt+?G-(?Due z=(yneu&RT0g$MI?a!xmD3YIpanqzCIHzc`V__w{sUHr;#|K}bRMCLL!?UEgPb0eoK z%3)am19sGG*jRkQPplnGD~$UQzy1C1fB)65ensS?pZ)A-|KE%B;fEiRrx3L}>Azki zGC)bzAZ87cP;7@-%He(pI8g+l`5{;bDjh7Uar8lESzcaF^kNQpWKUjPytcfg^yd0A z*H$09ck0H&`wzgH6}5F%TLk;fLbnyd2dTmjW(og_J(ZkWy(oRjkcpW_(N+2pW&K94 zlK7`M`b_8wvS`z$4r?|$W4j1zvL8Z1-gMtM%Zyi>D)PEP zp{DpaLL-K$xSTiLpj7(Vzdj%N!Vjal3Y}7p%}TR$)cVE`fA~XD-`Xjk)IuXDI8@O| z0V2$2RpkJuQT6j{ucRHTTXJww!lKK|PF_kqax3%fKlHv-)#=2{`;IkHmseI+a*_=` z`zo@q3&+t12m){v3z5FVNyR(@F#LfyFIe>Z@gE&vRbgkf%{K3J!AS3sJYr#Kv2JnE z!QoZ8OL9>7f?Qi3;i~sGnL+e2-7aRcY2mgd6(**dheb8{NOOg6O|FTfm6_Xsi0l0( zTntG)GkZyye&EJ$#?(tA&)EAWy^tC-_sN>&a;YrZbK<^`tp`VJpXnZNEc@s6%&jSW{~b+Rlm{z?z0zT~|Et+;_&Q z>{9jVn^$iH1P0LAz%SGn`ZggwBd_)izu7bR?&#GR1#Gw+w5nKo=ip2r`;m zSc#40(uMy03a1zkY&{TXlV7B5;X8#jjSP(pU!3L8+}8t!5UPnPUF>F<+p3x-#)?&8 zi*GJD{dmP?4y(MpsP6IV>m%H~RTVln^Il_31|EZ%D46y2^$cv0GB86pCIA#DsT!GV zu2eA-V4!$hLVP$ch~M7Zo;{VUK#_BJbzDpw1VLcH&VJXG$h}hEyul-QDNf#ys(-p{{+Qrkx{|eXLReVx zyoC}}WnGYTShLwBs*`TC8*9?x77D9xJ$BU%F21q%!u2cH?d+AoRalsq{<405_%Qp< zKN9Zu2s-(N_b$^>i>=(oD6yuclXTLiffLr71ub&&ov6J9NEZyQHa7{QLm+ zE)_nQFL`SClO(!IZ~WT60a?}ovuua>YGs$*X4u=9Mc1qYFMr0ngEcK}t%IW@Ce2Rp@gMc#)UKDGsP;ehf+E%w zp}z_zQ3Y4@c<(7F%`c}OMSIS_eKRZdj0UPb-I>|<8f)_L1P#7=_3Ex&yF{WeAn`cC zwjCa-a$JMCz=DbKp{j$-1BT1q9aJjsC%4_>;Bdsb;7nm$tP*R&j#;Qs&hixPP)EIk z+Dd&}nGNR#WthyXS-yRsLDTb2%QoL8)|454Obq*z$%TC_Db-TycJtzbzVS6pJw*7J8 z-4D|r^%_(ErO&SZ!&lmQMahg!@hiU1JKb&0uav9dwB>C2S~R zU+^@v;W(pnXwXL=eT2^ZCqMZKA&puMibUii>^l*Wl34WFQR5N>SH!rEBU&&`;V)7o z>=4+^vy^~^hhH3?De8SFoH+LZ8FagkG_9bKBX zgN7Bd>4d86Akk%Lkav`$H)?cH=D7t2E~nOQn^DHWm93MYT{Yfotch~x)TvX44jlpx z^6McZ+ivoVWS}I0rNnX0Ulty8Kq?Bc>1U5lFW{jvNN-T?-rU@W`NieMa8(?o+25m1 zx_I#-yp+0$@^zJ7D0MVed25*vv*|=b@Of-(X^eW$;4R(Xk3s z%NBb7UAx6k!<&cQFJdE<>6oM=-6W5?hQvAP$S?6UWkCdYs`n2@oS%I13GNf1Oh78p z+JTQm=K#5~IcFV5(uok1a{i+EWYbOs^zR5tXU&==9x|lV%P*K;K6h=yl%0pil^rau z0c+lWCVY}B{AQ^Tw-MMWT)Oq3{_;&br=h-qA$AE>V0xqIvh>a8`cK%w!<3<; zI>b-od2vZr7AmC1Q%y{knp=TS1MF4Z##8Bm~uHH*S~>3505 z;nmyT5v<7vjhryXe^ThUsZ%CRi}f3$48HT~D_`aSN$efr7g*SU?_U;w_rF4}ST=6q znfbMs79BmEb~Gj+24Sa0ozA*^udyZ^^VY3fxIkiU0-Z6eL}t%Uo#Bc`sj8| zw(rs#sMqdxc<}9FO(RpcZ~XUv?XMJS_6#|17Pf0}-~zdc(#>DHBrA2!Ju!06ow4W7 zOgvcTkhj@+P3e%lnLhCrZD>UKDkM5=66ia`FdTM}n-o|+nGJ2PXpat&x4B=T!;A>-cON}^giJIlaj`sl zt6q5N*z~H3BiH+dm-h?KmHWQ}_;zk7FRPL9KrDTp5!psc3NnSrueV?InBv0bH&rb33VXvikefu_UlE^H9m>>_)oCAmnScJpESfM|V6t=5Q z9Lh!Xi4iBf^}&M&H*ekqn~GQyRz7jkL?U9sV!)b^2r*}q{ixn%b9p|7WsluIShgeb zQQBv2a^hU4EcPM_Z|<9$D<9~V(l_j8-^e=yB5yg3FR=59)>5cRgEq$2JBOB?8C83K z*5$j0Z|(NnynEQ@J;OF9c_&!51#8j~$X$?HvA7~@!MS0>W6jjcjPYIQI9Ht6aq-B)#SZcxy znQ^S|Y~aa|21yL^ZRehFT(`q=#mTC}!gFEC^g6I+_XWbzTH*ZF+x3@k-?;rCBYVBv zIml?rmOkT(b{@TW>hisQ5u5r3<;eZ#I{QyCezUS<80NaUCz1Csw$$&kCULMz3vx|v@f4za^{3SH zS{!byUcH_9cLZzl^`_=#lV(lZvZHF{s+CaBSkZ)^BXA$%7>I@kdUG({P+};NOPjNW zZ6}!;J@#H>O?K_quU{uN2a-#5;X0NH@KAO^sJ~&3cw(`wSO4jte4gUBQxl6%f?p!V z1Z!fY)TeKsVWva(C+}=H*MM#S)=Xu9DSy>l&^}N9@-e8Ecw_qzp6Xq19cxNu3jM%x zqwo_Z!OCS@t}s`9TdSFP>!_n6c3$?Y`*!2iCwCt2h%7I0SXpeJo!}AMCifudNq6A3 zMUN1EA5+~aQBg~#xL(Ro3?RsR7bG3lYyl>A%B_xIO}6C~Zkdm~Yp=O)-)o$|TJez@dWpU`W_HNO~I{NiR_5%!kY5nf4UWfJk9u8{M^YaU2`Xiu20R5sBCwB!DK|5=aWi19}o6jw#0BYBUZQrF|AGsv!Zz%5I_S)cRrN`yBH37FRuc`Yb*4 zV7Dd0FBb@PH-rZdAJ5D^Y!ev{w4 zEE(1?LS_~qF>eQt&|%Hi2D41ofAeOG+FHU1D%IykMhqiHTXT;AYZzfFxu#f+rJ6y^ z-{zj890!Bt_Sz)f}PAqT~$gj+m?3;;FIjT<-i@81uz zmiL#HIp&o+6jqm2_3YJ?DFZ;nhKVjyZ$bM!3j-P`uqJ|p_a1BVvn2*n$spxlWq@RD z6Kj8%Wyn^y;+-y=ZtTAbHBK%#c)(-ZVXvwoCNA97wxJhCkD@(CR3lK&Mtu8~?|$Qeij39eX7(cnvDI9>c!0!8t}tuMKxn!y@8Vz6_qw1IMK&l9T_T7O zlOez&C77oJA_12yJpe>3ERj%(9zQst0uz&xlGu&&6kJIB%Sm;_JX*hgJ>lVqqscrX z@=;Ds4voX~kRM_D4m@Rr;uWac(Xi7d<*1L+Q_|}54qPodar4TJXAhq4-g7JMn2>r< zxcm6|n(C{5E3XAFI1#bvU_$oZ>4nF_m+rcD#kGA`Q6`n7oA02eq#(CE>yi8n)^=HBxDV$Fg52Ub>89y@mI+_`fa zcpV)OaD<@?nrB;TEbTHvNzV$ilY!-o1MV4je#-KD57cruo85;{}Dw3y26U zVokaUxY-;HTPR0vivAr$3%Y_?YkX%54>o!0?aMWr{46aBVMDuzm){IJeQxx@dl&CO zjq~eHl{*%0aw_yxxVH&wIy$*TOo@$&i>DJ=9obI6)95BGzM`E~bXc+LD{N+~7?)R%0xLF(oQ;5F{*xa783UmExWjZpP_BfBfSg zaR4HZH75=glzi~P2j72@@IWVw^pAe@Bk&WOZHzv_ktm4RW~0?1!7WLu3?LPqJJM5l zBQTU()Lgo3>59~4wTri2sBgHjr(x2nn_m$0*VtsGO-cFc>#szt$u3%EO)Rbf}FZ}qS*6b8O*+L^7#F}6Al$_Y%B^>h-wz(W$?w&qnn7)w&$G+rw z^L{#WN5syD{UUDn7=8R3k5Y$`(@brKjPUXVU3GkT3>Y}zV9^0#Q^V1;4gK`nX=BsY z&tSFXa3Jij0FmHM)^t30_=`h2K67w07Gl6O%5KDvusCj!5KoF~bj&?rh6p>zAhtv& z2~H&;1VsYMJc3(_agO*4;zTKrcM%!X>%dKR&g8R`mz__kJuz?R!&~El0#o&MabdSLi=jAFkTle=Tzk&=qtdXeP=#uZ8Y7bxYLmiBG^ z;cv0N4r{i7_zXd8I~Z6kJOkEb zN#LjYQTvaM*l{3f=aWZI5F}qdc;WEK>ir&Drv=7pk2OaPmK;pf+Y#R{!F!N_p%J#W z=>FhNY!bR|udBnFEx^Q1xz$0e$$VL!>MWccDeQ7PlkK{4s;9BB9H|~4$=}!K*!lZC z$6x*0??7MAqQRr48M}-z8?$KG_*oryPx;e7|McM417YL2BXjclbu4slb5p)?IWFWF z1%V4eljzM^(~%;Hk&np3(@=A>!A74>q8hp#3Ms+t1obyilF0&U#HF&dw3Gw42)c); zh@~G_A^Zo>6)>kTOhPq>!X5n^_!2fqLS>J>XiUhMg3yeHNjr~CtT?mt*!BDqiMiKp zHwo5jo-Z%I<~^rgUU0AX?5kh;>>U)ob>M_`pNw9WzhUdvngd?5m6w)53kG|KzF|M~ z#$;?$i4JSFfn2a-Mh`9&Ej)u&8Pal=RrNYtvRZ3645?QT^Vo}EYpn5X<7!=3^6p?w z);tViFe(6@cm?_Sj|+wqs3;?m$$uZXzgwixZL-jPy3lRD&_7|5)UB(I*K3vF?Ez+u6uJdX7eDs|Q4+GJSmm{1B6MXF`eO3SoN%Z0{Lxi$|rSLZ#&nlKc1 zlOg+tSDqVn=u*+yi>EIB@528S7@WBff9x#9q9A&}0Lm5`={VL5^0O2!_zJs+)US41In~S2+LR!y zOmHS(dCi&L;kUjCIO;lavtN9!G^VY{IEP!odgv!aOM5)dYuDg8eSQ8vz z=QP-P*bpaYXG{^8w@gbBrl|dDqrNs{x=j?6Sd)LTLboL0w=<8)BGaXB%G^ioW0cwV z5^EYst(IF4ZSY8akiPHyJ{P~&r1hw+;kK&X5$YkBV`HpIe_>Y3&*v6w_sNqdU`-66 zz?#@FaqcB~)Nrt7an^uMDQVn}20hg;nH1RR%ME0l8^pgwrn8AFpkAS^`S%oS8cB^} ztRp!qsMvbt@y$o>+)~xVdpRq%^1zicZ#wXCnhnk~S@PPx@_tZ8T@70!ExWq8ExVWO}C|8 zDY|dQJh~cWlL#FFkq9+JjEKgZMI3P^&=8o2SeJ4jDJo4MBhpPwGr2_xIFi34+Tn5j zl2DfeI>D@D5MfduB|ADIq(}e*wD6SB4z0yS$8Je1I<>Io@wX4anv<7akrWE%E1wvJ z9_t;jZ^@dIS8qO&j^C>nwgsj6h{O%U#!Ow2zX5H=frE$o_U)tH4}FJf>9A%SIs@x8 zY&cqYfT_Y5B@&@V9m)};w~94+YDLxxd51KF8gFjfCgwt8rYu>qg$HCqKX3b0iYrxMj65qmp15+w}i9xz`@-nl9i_B*K@6@5AB6l-$ZSXsE%4hGL3%BoodyiG; z+=+8F!#5r9+?HskJpKoKuj-oSGJyWb$ceGzh7?aVwKY>(HK6Zf)pT@n9zSlhmy0Fv zqAQXPYjz5tY@v~kV@(cG7OwaU2Zr4)a?PIV72$19glT3 zby-Bb2Xko7mc8cI?nM{VeW&=s#0g?d2qdJE^a$bsu@~q{ zSg=Niw$MSKTdbp)IDFpRd2_4$%REfm=5XD(@%L1&sV_BH>acLzkacw-HRleUy?*Ih zeSQ7)^VbT^mX|oJnr}T5Iz#qW2S0s^x)5DCvjnQL{-OS(?-D}2j?WuZo2cLWwVhpK z`n9t}hc#P(iJfw*<5&}Il5jmtI5h0huDIM8elh+I3=$w5>bt9HpWOWNlZC`q>u{~ zQSl5E8jyO58Zn1+JnnCA4tOM^uo3c7`vu- zK<4z+Rc>=C`;RZ`>$l7%miL>cxm6cP6t&c{dJcZ!B@lyR- zH3S#-mIE6y2i;gD2^jiTHZ5-*YkH3#4c3fInwFKjDt+k^CMuH|g{&H(jJ_Nk)eTu@ zj1ae7c=}xUYL3t?PUtpE7#Lk-IVBx+EpwZ7SM8B5&%VD{6aI)3I{Z-sYkGNk0T9K} z&JGIOSy;0QGO7ql1voEqM@n4XT5ew8ex*?3)nFt0qRhnK5co! zrfQrF2BSF0^KF1yOr{R(Kltfqe^al}j{JLyHT5Ke);q7>H*X z`XmrEuW@RWq( zFD-y*MeTf4RP@^HIZscP+{!q5WB!p#Gxlbu9G8>`k~!BuA6fffVW&PydLFv&+qG4f z6)`(;2I}dTxpq_47c=+iN0jvoTw&`s)nUZ=H+&v?lO3mhp$=I2?OoHKs7>aQUM0a)KH>Q51FFCOtHpn=v4Rc@CbhozbjX- zuvV#KP3ohT0-kETPW4`m3_CY*r@`B?(dyL+qSSTjC;ns6;xIO_TBMxUJ}nNcIHdYT50n7ZT0vD1zTw|a)0?crY= zFnO`P7m?1qk!xZPgLarD4-TNcvZBe6`G&D3U(2ox-U5gOT=Ew{3Ft&cV`F2(7MEMB z=D?%h|Ni%c(j>Ab(KLVj;~#(c;fKHa)vrGK=%fGs@BjYflTWD0ZwF8^kFfj1D+ALn zHt=FXT@L3&CQ1?kDCmN`FQ&vje! zyxUwMVf*uw7jKSRx?eA}q^Ez@gNKg{qqh?ONN-GzLtwn!@F1zAS=W&!iTrj8by!m? z`m^RuX zne)8Bi%eteW<;@>_>&|u<6ryNfHi-WEIfIx#F~q@3*Dv&-QtD6#qO3(SuR?!z$SQ* z7Fb!|0OEX4u_l`$LUDmLQTM2iH-I2Jct|JGoI@tri&Kc(W@C(q41mf!ZlrH$U14Xt zW3cxOpUu>7J?O`%heq z$*t3yu&&SOl_izCBr(;4!q)4LUhWzgW997CDhqw5DnPNWo&s;V{l=1*wst^&1F3lJi$YLj1LL^7uIRTLhWIi-qSeu;$v`g3@*~Uid@oL0Rl7jjbMARrZ=Rr7}Gf zUm-TwS@h(CQBAh5wZH!LubnbO+VP8+8XQD{a6&@`1~bLL2sWzf4g}1!&u@fdYWz&_ z3O2QHvmkjjtXTtV(he&V{i4xM<4b~cs|h%0^hNQ z0sZik`I}w1zS}sVTeJ{cDZGH1LfKK_2hl>eslqR(o{>d?H8sUDkP&ll5GPs^)>qmg zqY~P60wpn&lNk;5bZ(#7!&}6f1WagXXsD{HLWQJG2cR>WAaRohYGG>*I~L&!l+*~p zNM{kXn6ByGy*py&XHTEa3{J>2TbN~?nv;~>7;Ew(8o?Y&{f&+Dybcp6mDvezL5M>8 zpMLsjiNmr@Z*O;HxlDe{r-nf8TZWY!*>Q-_0r(bNX}EG> z#YWyzXy9yInni=FQPyg5FUm&#REIu)&Rco4?#!YHlqQjcn zkE{08>>$=;%v4lV3YR^Fy5Ua_r&netR&U+fFZ}Y?{)c*vJ>MtnMvve##(p{WKJkPQ z$0q@xdFap~PH;dpE-D|i9STj=d&4?(L^&ijX6&C~GGglUB5|AwV-?l;pZ@eG<^>_! zs73|;qAO!t&h`v`2)xD&o#>l5nDZAv3BSUZh=m(LFFcck6cgwPJcUHeFR}2&-^7MJ zvxFm|!oIP>`4z(6K%r{#`* zt%g%7^~M)l2hVl(o2=+?sHvvuVgUYs z**g!osp6du)G_KkR$y%BpK-fq_H@iOVx6l;=SbK9_h3uEFE9b@j)H;7oXASw5Z)!~E@hvpj3 zkWmk!VX&0_lt0122l2He(jZq!Kinn!U`y+!b3*Qw8!as@d8IX)i%$(*a9rwj zUUSKr!E>UmT-R7T;And8!I}oT26pD7b#=PHEh>KTKCG#XP=6mPtq#<0+F@{Khsj?5 zDQj1Z*9QD`et=`pcsjhU3f4r&VrU|s>A~2*$o+VTMs0+2?bDSp9roOdg|7(K#OQ@C zi24ZDgpm;`3)H}uG0Sb{#0?1m&EKpQE?pO)rcicE_!C$I2bk!ma3c>;Wc zxD^wjp{K$)_}dc#>gnsOPoB0lZ|3&mS^5U*UFG=&{m!Z=Bd6A^2&s@>De)6Z+&nH}*5qYu%%J=WxF;&)SyHTf=N7?IWa zR&!u(P;lx)lQHvxu_mr99vf#Dj0lNowEe6%Cc z**zh6mu=0BELS@R`c4fTV==y0EM9$BQyI-M0BoPB_bAlx?mp|tps6lS!=iYfd|fSQFgYt+rz2mxwi4;NRHTh^j4HEFwM51GgIa=}6U~v#hJv6l?11>K8d)$QXCJBr><@K@$#i)^(jfk#fZ?6UVxf z;Y4|PfZauwvP$;i&=WZaZ9xSmmua5${O0idumAStH`M&UFTdR2O(LY{D82fyrg{ic z^;SK^nz+R)OZN*klZ3npmGK^TZeCyGv*PfPBepAwznXKyXl3WrLiR)< zaky|jOK5#0l&lxd*$TG`^2$@4tJlsDW=?K$+H`w!Q%e&uP8H?lJC9vnvGeSyQ}NhM z@%Z7R=TQL+-J)=}Dq}RAZa*t{-kw!X0rnOSRz|~!;ptWn?JM_T&Gs~Tro>QRdT5Dp z>dtAN^G9oeJv%@6_IMcECvq2CkS_`ERp+hSf)Q~VK7p#NNnbQ!aMdoBrE3PceXp=4 zWF#jJfn~r=w^?ozW=@;!v&?tmQ0(v+iINYBG!am-n(}XU0fkD z7|Mek#j#2Z1A;_GB9ID$Tp_EMxQOR}{^x&KCl1^sZV8hlCNwxN4XF%IS1^0vLx+8L zHXYE)V9MM}R>4r;aED=lxJTvpbKjsLlA);%C)p4E@`~h!TepcQ$he$wDLm!;xVAz& zeOr0n1lbX9$Zy$)gOV68iACc?Qz8v;)4v^VaKcOS_b&%5^;mpznMQtq#LQ5>4;k%y z=cEs7s*W1ftJy=W`Q?{iUMuwzYMg{zmuqjmX;SUUYs#^se%3Sjk``7o7>2u z;X!6(&&7>S1P&OB!G@)YT+)T8gqUHY(QhopX{(Admkd*a%l-0Q-JY>DnJ{K}(l-Qf%R*0#!_ zIS2-hfc6RE1a1;)Badm+BBGA{bW7U_&$yvg@8@M4b0RA=H8b5eFYJIpsF$%jk&Ci&HBMeryXG)G>r*45j}4DXS{e7C@d2YZD?97t z(jzg$_U^X`8Kz^YKniG!<(`)n%lTd`N+5bc{E2DPb)25#2;DCS3`p9sD9LNUS;4PXUF;wwWx2X}In`Yg5NFCvTsvhWvM zDK-w8fLYm|C}y|2knbsEuN4~W2yn+yj@=~Ycx;@Ius-MPhTEZQgmsJVEU9={nOm4! zT2^}L(k0j_+IvDm0y__ZWx1U^du&h7o|l*yn5}jR2z8GNatm2C#ebNwO%MC*!?()-80U_O)54}sQ$?#1{@9C?6HF-m@CX*a$xXqmD z;OaEP%bf)8ac551jUMs#)H5st`ooq-W!G+scFoEL;V&!OHb(#T${WT$`-8RycZgs_ z!ePMUD&Pqp2WW=X?#OId!1E=m(%xSd)-+mXvM$r_LF)srQnS?~QZB0Qv3Y3t`p- zS2dK@+v*LMcNWXnQStmW#hRviCb<(*F4)IiJycX(Qnh*0=Ios8q%EiOCZwc~J4Hq$ zh1VC^5pq#1=Tj0|C;y=-pKd2jT%ldG$vEEQ>*MZUni%w9P1T{@D_66JSo1%B@wIS! zp>TCl>*bkgCwA{yGyBx(Gv`hll$ZP_JN>u$IR?v?nu^*VqlnN$*zb)SH<;$&tsXki z8Hz6$YZ7DvTfck^^fc zq696Dzd%6opdaxBJV3AzY!E!k$x_c{KEA6wy_TmPUn%5!3mLvb^SuWT9^fd4m{a|x z`3h^t#T+#XJ3LdEGyb0ArN*?X`l^a+6|C7JGacay&p0}mblm)#EBZMz|{Ca_{8O#Ks`7$K6G+_ zfHgsoEGNRBPN);rxC-Z$tu7K9QphLh(C-FR1V@zC4Lc3?!$QYoz%O zN?$#=W}8m#`k{k6W?k}))IaaDYd&vq+7qlvc85i-CPKY4So7{h=fp#syG9>Nz( zB$i7IoG8W|oZz3q!;bU^cTxuo3c>@PB9*8Bd_)f?)Q61PEU$!5;>;m%lXyzp+?3;0 zz;6aC!WYU2VB?fp6=s4WkK$VB|@&Zkhz{rD$;k*ug#9b2LhDoXHPY2>8c8{!z4Re*X8*cWnqQPAimQ zP22)dTY)~XY`j+rg?u=s36W0<<*<9q1CJg(YX4;wYIgbaHN~2GI(nx@96D+pacX(& zgZmFWmwBcqryVd1Jz%+Wm+__!-x^wB0ufD{L_Ub$R917Ci6n06H!%yp44V4Q{Bb(- z9rT{((jaLc)_i5LCNilc%DS<5vQRrsD094)I4$Y$He18NIXQ)`4?k~hey_5uf5yfB z$Bz8o+shaU#z4Zxh7TP8#0iiK=q4Li>Ycn`tcf(i1Bu5R0L1IyK!?Np^{;;=rWx-! z5R*7dtd)eK!qYflgTx5{Mv>I0%?M?B;H?3m7%Z`0P#G@A0q>$M_!4s|K$8gwG(2(g z#I;LB*U#ov98JsG6@NQtd)p%Pg5BWt=H}+&)Mz2ecVq3M1;S#_hd%Q|3%pxBosJx_ zUX(J%<9JqHX?}hl%dpViuU@?xA0H32MSh{HKbDE=(OX$7Lp?)Z=YTa+g94Ur;l4)? z#W;_esbFzX*1nT#eOR*-!pIe?jx`a$a_ycu1#2QK+ly_n5;_)x^_d2bcaBC$!px&k z{``KvbgXG$WH@K#Qm;*ZE_2*O4H9_?ruojzEorlBE)i@Zn51n7So3^^@TRBm15e?% z%S&`TBQ!OJ5Ifl3gbda}698y}=`phbbilIUDrPL~gA|_f$m#h}xP8wr0c&bY^%ol0 zX4{RZvrnl?ow0R>u3WYzcrK$+gmk#Z7}D+G9khbO%5yqw8*KOnH*Aa7{CfNeoBfyl zQ)kX`<9(?N3vQ1!aa0kPsn9uCa!i^~`1||B2Y&J`>_vh380kwd`{`z{8?t@I;2tgP z@9@U$3j46;tBEzg)R5)`8VE%bg_|zIwW+Pe6LU{4swm2wH-AA=QgTk#rn~iT-Maox zc6z^)vHjQhnrdlEL1TDzZ{EC#V;6fKhzd`153$G##+rCkm=Q<>4#KElP=G=>4=59% zfapO(BaG*#Km94&ptF4?sFV@SL*f?4U`bo}B-%BuNY?M8lXCz)VVJ;BJnslBxF}W( zobqjcc2A$G&~w!xNmqBB%MDFvxV%=#@f5N)Bf=_5^IJ+ogzQc64-V}Y4#u~hdf57C z>%(2MgUhD+o!qIEXLx5WM|k-S?Z4DYB$Mxr@+zHDRkk!)lPwjcElV% zahy+VWu+JnuEw4Eux8gpQxR)Y4Q-~g?mESq12rTY=eedIwlY`zoLKnV=RWML|6VD7 z=~$Ch=QEeP<1}BiV%ak9<=DLljsYOb#N^LrT_wM!R+|H-?Wn7(JCf06)a2iPdz9*U zM@qFda9EIZSAif1UFIj=b5vFOB;gLwpO3;%^jf|h`ltf#QRv}0{d@^nQ!3Tb*Re|* zvs(x+t|}g8|4i;7JV&A>jQ7P_*%?_7F!CO7NQa6K)?{J(s;@oBPjq})w6$&9=0Kfc zO#mg<0)_Zym^JnQ+2@wg9L9~c;QNdtN|_T^mRagKXX###-SIYuil(G_ zS}f`;hSgxA;rL`nn{>OSJD0H;-Tj}oE*)Z;Ihh5eO8vg$Gzb8!_`&nRXKodsV!qc!& za=>axD#ThJ3Q>tM_fP@*1%F{s$O|M0SQGJtuRJCuMvVI>vWQkV<*82{Feogp?L>3P zdh?mt4#9Q8#hAfQobKY_yDuEZo zD1%7txp3^mno4N?6J3g4A5Y^Py2%a~ja_6_#c1zmX77+RPd|#w{ z_oZXa$@6DUb)PeLmFJRG%U5skLn&n1GuywTs^pimN-$yC?SG6k9|sH_yDa>aeS?+@ zpRBy3vof4LEtnN>jiDMt|E5o(aq)*Y9Eq^2N9IS~2f;F4eX@9j_C9kPnbL_aWaZ{pkqhFky za3iy3*M^Yvz~nO{jvO5pFhZNAY(zQ$&Zh6jn*d^N$1*7??Pz_QJ)oR;)Ws zSo77ynkXQbx0(oXmclhBpS@mb@$Ju`EQ?%e{1zB zGZ$wA7FCnT9SnqSiA$GQZ9;+7wrdX69C{!isx`hceRIrU&1Vw#KEGiJ1pMfukBD&P z;Do{X{~jOYxBkN){va-#fajn;%MO%qykQ~+g=g}L%G6|$BJmjl|w*qvOV!0T4Qs-3O7HVRu} znqwack5&pkRtX{f9a45g=jP<*6ZA!}CLLnb(uEaATYj*hE$7ka zixYLc!T>A~Impr_{nKfl-&o0uQD6#R0l3LN;J`}EoRr`|Va*VO0K+H#k)A`DuE*1} zHC`Il)X+dwC}2%UN3f zS4N^DYIlY;xrD|{qBvRq+!6fB*O8UTPDb00-nA~YG$gNwiQMV%HN~0;xl!69lSUsq zYkRabrD(^p-~`9RHO~1ZPU$1{J4&RWEj8O{tJyICxxr~grWm&V(dnokm;C}J$`oU+-ZXA!NzE?<_f{rRhTh1 zZ&UTH+U)F{6DLjpVR11bp-3_X%@JO#G~hYaCcE;6+7fLOb7S6%VBD36#q>0|Q?a)?|GTyo3wYJBZuaZf?>E2s%^PpL@n8S-U+kPt)aJiGKHh!zT|_y}IpCaV z%a}7koOrZ3A&!$(JFjzM}>ujVhVTg0HO+lV=kpz@W%7QntVjQ zDNet&Yu0XFxO4T?AeWJ|y8C4n>i1zy1%h<&Q$?(afgKI0gZ7V$7@(*)?d7P%)?6cb zz4p2F3a%`C?u1I9%6zY|rbs<3lfy@in7_t*`GhOu!WLckdem1*;{F-)q1%g+= zn~7)Q&4M3e&1J1ROCxmVZ2*WY{K_QbEqnfL_}ohS8Dp`OXdA*3h=Ro|_c$)R?GKu$ zh%cGCKu^)C*V%XM_41dCH9_7A0}TEsf-4a zd%OfG)zHvDN@Ut9!J2dq!9{3GO=eJsLW^(eq&JaGgZO8s`^*M8ojse9^4Ae-A_YO= z7P{uiqmL)r9lhk3aN6N$zGG_Xl=Miu9h#D7@>jIIyF_Z>Y=}{FRMFT)x0cwKIp9XO zI&UNWQr^(7gh;)AsSj(ux>!>pS-ixf;h@dk-Fk&Vdgu1}Z4dMTO964>j>U!7?a#kI z^m;}A3rW9Oy>d8t#ZgDGXp-=VAVTJE4}}!GHzY9RVMgPTb>Uj-?Y-9h(TWos3njh+ z)&q%u@gjm3IlyqN<{>J8m5Ut26k*Q9s7bIUG=VFkrE>sD$tX-YO#qi9IUyp5PB_Pe z!3nq!UJ5{5y=ryX>di@8qY+ru37I7UaSdnI2}!m>w0TQS&fTn)!bSVoO6OU^yr~ae zDsERaHrFi?mbWBDUxX$`h}NGaV}cw@_f+-6w;x7KE!}O!f9! z5xQvKYUiMtqvtEDt-`fFtl2e@RKc1ERP5V`gwDF_3~RR4|5ii#g`&X$0gRm-)6sJs zPYO@_(&IH{&qLs*>#~KjS9-X6FS8r-ozg7PeB1f#550vyZD}P3?qjSOXSnGh3xj^_ zd0)qKuZ{=iVaW(hy`=u53qN)`;JF@5DeDua#xg^g2#Pq&0yPx94LF6HSZ2#a$Aw4m zHQS)SMmrHM>#O%pUM|)oC!D9yruR{?8sBOFrVLM~_yOYP7zKsBe4Q$h^eu%?x+h3t;%oxF}%lO?g(HF0buj5-9? zyf`*K#r}AT{fV0C1<_L?I@q*RN5?X9nB#3HyXvv$QqH-b_s~CVq<_drIs06tKr%#bNNp^(r5dUl&TJIQx%!IqN+ zk%6sI$d3LyebjCkKnUJV9%njHGG3#)kIg5U31Oy-w;}LI1IWhgz zAtIC1zeJ`2FnZVH0I5j#{gL8w=Og=ENFJfXv=Xu?6wbUc1=K4uqM$hU4DBE;7Sai z3eQ361b`94{dkM}{xa$U9p$2k1vf=Zo`c2*B^~E?i}D9XWXT zaAY*+9oA93@O8wR*f8KiyrZrDuzbh!m&czicgm`BElrvc=i|6sUso}cwyuTli2PB; zr_7=Y50?~|+-kbza&v}lj-6%nFzJ)b0jd+McWU-w%~uv{;=4#rPPvm4aX&(@Y@^oh zC6)I5BUqN7QBjC=;;FNa^N=cFocsbU&5{(H6dh z&4N}Oc!5}ljzA$NF(?*<%Fm#H!1B<lehNd0vOxCUY_F>Jg$)yU`WbrJ*Qh8t9 z!kUa@1~j_V_YiA>U{EnfO>}UY<=O@`b-qJ;60LV99wz6}@BD?tE6oocwmABiv?bT{ z7K|gWYpo2E4*6F61ccF~XJqVOy!P@hN9Vuo;Q6&V7!nKw?-0G}Fao$FSQDe#Gw?-A zGW7InF*AVXtw>4JVd)rag=JgHzjAl4zAUWC%m*6EV@>cPzHbq0LK6TY;Y!M12a>{M zX~@BFy>;srW&q42?EH(j2&hUqJ;14nl)8`tL+hd6JxF?l9Esz?X&r;g1T?{M33-Np4e**Vu9T(>=E zXL8a)_ppIPQ*;riPO#po*@rb>S**ziCYGkDB=*4`-O?bfa1VaKkpV{>5F`wusLb0s^qxUjH*WrcvMo~%Icoq?rpF9=p_lfXw99l#OP2&M#C z5^c$W8?P>!qGQ zUy_znmr_(;b>q&h+c%Q(YvXb&kDhNT3Tr9ZbR*uo@Q}y#;JgiKYtHM5f~8Qr;J%P{~RQg|^ILG_CI;)`XHwY+S3H za6C&E?e|7*!ejmeUm^0sBkY=OGuwpHSv+I{XPcS1d2u@T=(ywS7w@Aj|P4r{_R5PLkf_^Pa*m~J-Bf0W-`+u0U| z=GY+=NuVP&+hQ?1ZRCW)Nn7`BWxa4iON0GI2g4&Kz8BUQ=(TNW{o;>4toh1fP1H+t zZy`HCh&H;hU2ypn~jvr;on5`$zYJ-oCMZKxWMLlO6s z=a3I_3%1Mlq&3k@{Ohw_=y7tF7C3Z}aF1hY_3XtSmbY zR8XCO>ecMSny)O@#1+c~Y^=I)KT@xDr&-Ra-B}mUV&}s+hq?&Xe0YCI&DH+NXW!W4 z78TV0hS!_v^FEK5YPd`yeG#iC)f;chTd@H;0;0{w9TWs8L|)*Q!G8k3VtFFD+<*Ug zS0VDd|M!3YH#v`f{_~%c0*PEml#BaPQWKViS%l^wPGBfv3zH`l!{`XdqYxlmfFUZZWIpK3Zb^YB`|M~>4isRljX$NA&aEP%0$~K+ZIHunC!P~=`tHzo4^f0 zeOOba$*X#ZWM^MU6|6}x2gA3s9>j9hedx#)4Ev;7_`YFHCNQ?E&U~qmarL^*KUmcA zgB8Nh`~}O1$GcN{2>-SA=C>=N_V3>(qd&d|)XlVvbSE9__)jO98k^uOhONL6M7tdt zEwL{^t7o)BP3%p4iYNCrD-u}zA-I{mzORB=8CQl<8Ww+?9lb3}x2_9e@9;hUv zHYlGEB@xr$@@DM8e7k&2@F;5}sfh+m!cONAPN7CYEEr#5w3xVZu{Y&dlUrZ_P;w)J zp;?8nCnDm9Km4JRYARlO?XV_+v!HnqwX5=uy2d&qFOD8|(RNY#V&4tEq(WrLaNWJS zF;(M7=Gz~SJFdESMU~sBSf>wbswA#@P)Y5zK}>x3KvT{heCWji8i&?Rx^b<_+j|9Y zlS$p!c(3Kbpc_^1Tu6F5V7zz8drhm~E}Z*+;jSht&2&46-%xQpyxeN=H(bL+Sc*sy zlO!h?@UTZYl+Qz&RvG>AesYEsU@ z+Mwc)(stFD|J z-LTwW2uiwcSdjg7ey(dyPFoUF?3y??xE)xNyFXXzu^q|;o5>E=j$WQ#p3{9+PYtv+ z?E>4V@;rTZ&914W3f5$7i6BNvcdBDex*{Azl=q!EAC=rl?Mp8+=aH6F$5_W|gw?vW z>pBywXje--mb^LTEVj%au6ztMAKN%TUz&M5Nf|E3npzMe7i=v9S@Bz0T14kk#%$%QZ`vJxP)e z@s$i?K0#hyo)TvqSo8da^Q?RQeqv2N1j}Z-{0dJiuw#(3*|b*I6U`?{n1TXOT3i}X zw9)jmm1(@iWS7aRgho8=t(VUFux1Znq)JWIz?!VH!#r-FuSX`M2k{exL&i11+Offl zlj4q`nKSQ5uOkSpElq=ORKAmZw!g23?Z$Vmd%vBz@UPKeO(UHbm!+tehT)TLkgb47 zASPNa+zG{7M34YbU?mHKxP-ib2%KZ|g$}I9VKF%FY|beGm14OBj}qgK^d}Gq8z+Co z69f=fSFc%8o2sg=lIN5FJf01#iR2>OsCTK+VQ(PLsdlbg z=Ir*FvSGCOxX!#N9k%VTun%i?NS0>`Rl%B!tu6&KSd+MrXS(j>2TLxH+}{ozylnj{ zvk@=O=k>C%reu(0#L-bx8mC3x+Ba_9`1TQKBAw8`U7RQVdGuBmp0|Z*@{icfje^FS z7!5bCkd0f8ygRbC|I|HZu1U$snC0+{FgeAx_{vdF2}|H7SQGh;rIXn0r*2|x$>5@G z#ApsG+7=SIr?9kS-nA2L{`9Jy60;sr^Xy!X}q-OEbsNQYcf_D!%B!F`wFs?Hk~bMxNYI+opHMk9yrK0q=or~Ba%ny zA28C3GPJfE_A~>(tj+U3>BE{T0mdFw>MqtK{x{HtO(};)ue54ud35inP0K#RieQ84 zymJMa7cr$Ht=R6owdGRN-A}GnymRjKyX(A0?tJ&ry8c(@e|pf()Yn?i{`n7_=W+-1 zYL*0`bYbJign@4wlOzX>h~WQS0v zPoD-9vNj()%E6i;dN>sobN%3@;>~dtmkLTR=U1+euMQ7rh@D;?G3H)X?ya))Yx`Yp z7oLqja%4lx=BtIjK3nv+If4hdHlv!B73I8}nKq=fIFFur49rh(ROA|fA(N0(|JmOUyZ4Vogl+on?NJ`^s&nV9hPtg9jY%wC)z@B+N;Vxd$^GB|e&F`8qqMqapZp`^6*hVTsqz?TLxd)@jQk1QA5(BA%l?6U@N5DhI~BH?0SnLNzkl*y_? zzQLP&Vj4)~PnIDNHh|2adP<8fq5wVNIo6vllLQ7i&(o)v4cYcw?I+ zcjvfAj~)sM_QC<9>tS|>_pd*bv@a|o{Hw3NIe9YfQFF}0`)^d0^*?&(FTTqyLf*RP z^LFl{Ph;FnSCLsu&C&3(o1p_Ej)9WQ1q__9Ago20Bv}B2TtLvU*$NMI%YZd7iTGtxhZ^V0FeqPR=t#bp_nGwLsv zKFB$IKQsJR+U|$j?k1kL=a5D@`O)WHM$g^747lSo%W&ZT1KkYfsQv+-2S?k&5 z*^jAkm|i_|LF6K@N4S z_x-aeDSE-0Y58@?v z4@uo8D(oOC`=&iVdC_)FHj{MLH9lr$nLhkX-Pr>d4(gh<<+I@HLk)2Z1Ik%?*{xT@ zv?@_^#PHUAhKJ5fn4ITimo;{7+>GTbz9=i%*7_)* z>F)n4FL^s6?5owD)*m$VGcjSJB!eA+o}@YgO^Qj{@q>xx zLssUIikRJ}|MK|YCvCBb5;cUk0sjwrHCj3x51vVfa8a3=lN3ndd-j9{hsK8POFkTb z>_|-b#vOOh_})CexaO39)4lq-g2d}5mX^k>NIx5WvG(wmM;n5K&1;19D}+@?Y9#wy zH?8^2l~r%&FMT_4sm-=|c5S{>@aqe}n$t$P?pP4HdRp5y=<~)cHqhVgOh@|7DEDE3m;O0xv($Esm~P$J%q+T}RZV}V*&5c%JUwA0 zY~h89iDgLs8)MYO@xc;8F*60Y^T~(P(3VClGkI`)O=Ku5{*sfD6PwEaS+N7tvexQp z|4LX>*Fkr9#z=?yNhxh=juEg`)p7x(>$$7b73!;O>mCbZe5dv?5DK6DH)EH&ti zG96GtA~kY0cDUiVkt=cWd1L_t?xfANtsy&&LkN@bfPs02&)1rXOTCJDVd`0GBw>F=}F@ z5R;aG8Zm0JuAB@TlmjdAo^!z80Gc>$IIv~lIz!g*7vCm~4yz~U=yI4vSug-UyzCcU zxGk>uaB5~kYH31RV|LV?%>8$-UTnBse<$zQ&7-r5cG~R^m|K|TAMnU~ov^`ISi8O@ zur$|g^G79++OPkWWp7?^8@g$h=~^ABE|2sAoDw4}Ms1$EYrX5%;U>1Eyy%q=(uXy> zCS#XalfC#DZCzqb#HxyRO{{LSd^`*?HVv-crM9|EyKC*nJS7y|q^Xlp%PD2-{&)pbVDx3;5gM#44tB9yQI zNC7i}Y>0*BL!izQ{S6;!`*0G8)?A07*TOXFB0PfEgEe>W+I{p+{y^Y<)lRK{CA;Qh z@|mxd8ng7ApS>aA|IC&d$n)y#n8{4S;g!8LWw! zv$K(pGJvN^R0iPASPMG5i;0;@ea4Nf4HtU8cLn{wURV=R_cY9-L+Z3E|%V}%;>0@j)4x}g$W{>ZQ8YdUIl$vQ-M$R`p@oSO-CEuo8d+` zc1R0%7#E%1*?P>P;jpn|>x{_{rj5VmkX-r2)#CnT1^pg1z28vxZc*O*{u@kOryJ}v zE!+CL27j|t>$JT$3>Y&@YW+IGP1G$iI~CvEYSWBZ^colu#-a6CNeL=$nKIg?ov>2qY?!J5NqS9d2BMb>?gsk_sXhtV6DX`qYwW zL@i|{&^K4M`|jNaTj68HCZ8Tq96YFIm&AEgXUmmjJ*$24qJ8L(A5?RY=CLEku3x{- zB1&;>5cV_KVj#eclIY0}-xM0MLw!zN?yUK_>k3^q8()bkK)2dy&U>*j0D2rlEmOZ(`<2bc-`k0yt`^vR%U4OUv-uo?0@7G^{=X}y1*R3(Nw$@p{Dj?J*tI~Q$%lnIj z_h#jP>o(i47ZT!hF?xFYGJ=?pHe6fG0!|PUhyy|)Dd7-fG)03YnlrKhk%3~2GEB@5 zS~0Eg4B-61`OY~OO_~stMO&sD<#@V5qF_@*QdLs!%?l+r(o62=)ih*PG*?}^Uv}IDX@4w5@AO*tFZ5x}u4&dK)+AzsCC^<7LgY8L_b%^;oJK3c!rxZ2 z7i4RvH$>Hey&_SaP16oXlT% z0jPzj20k1#hn`|L^o&Il%@vOc>;tz5{`4oym_VBoM+~kEHLTjRIB*R(i6A+!Z{>qq zi%#w84TK~2CeNO}9IUCS2_MYO&BatKVohizOL)X!mO;R0AGXt9I#Qjv*$b>QZOs>3 zcr5i8Zf%pW|0IAO&7OpCOpx}g;pFL~3dbPea8>Ono3-qKW+ssVkEDTSY#3jZR78c^yBPa9GyqHn9#*Qc$EwU7* z3DXCMMt3OJqq==qQ*Dl_V#DrY&B0pIicq6lp}GbB8V`?}2}e!pLv=#@M);O5op67O zSG9HP!*_+24_X@EzkQ>B?2)fmdYfo#=&pD_J^0O}M}P7Y{%57|(fo%Wx*ao|F%Ed3F&Cd<9yT39!bCaf{#FCY&m7$u-j%%ZRn z4w}H?(5SIu!b@@EP(siru5%7-MjX4n0}D6A<^{xD*?PJ%)+ZW_czFBu6=Jsa8(igAj@Od zdfdG@ch(${Yuu=*_kOf=(`*c%?H{REN?sz?BpVLIfo{+TbFUa|z&R|HO+LH# zc3M({!^TFjruyCnTgLmJwAmjyCS-{AP`(A972w`JLnK@tbAo*A5$LBwt2Rmk4tmVk z-Fu`?YBTcL{6l`ks(t=kompC8VMFfUAKKcgb?46T!-sXh1Ld8()_27^Qj49|=4Y)X zvAT&ViMaMy^uTCOPE8JK*le-RQrzl=uT0n(Lh-w1&;9>Ctf}@eQ?X%pvE~phY0L_P z2YU@}Y?nSfYAQq<-wW3ZT|Ob`(DLzjCXdJ)m3QNBtxfMY-+Qm3^uypS#tsg;_5){c zfA4al1I#VJJG5eO9~2KuB4^A6|R6EdcJkQ|Zr{!h z-?C!2R?PbM5)xKrK6=!$X>EvTG2w4#fkwSYo9ES2AJ*)evW!0f3f#G~sz^bZd8WfU zGr|PlF`d+Drh(F#?PM{lwnZVRt<)MBK6)?7#o}zpKc_ zx>Zd*tVx_7%VWv!$o!PYAL6@0e3sLS7C1`yiF!wxK!uM&S_*I^6CQ;X8p1~)sgLg} zz8p2h+`MoEs^R01_pP_x<)eYxr`GSzI5W!1mI^pW5-PI47UMNH&iq%!nke63O^60( z6m5zOKg^9-d_~Hu@)z3LIojz)85l$xn=UllVz@5Mbc?@%kA_qe?8w~Vn=0rDbPG?% zhc4egs1$p%e2I8YO?FzxOV$388YQm<)|40-4ZL-0pdbtqgu#NKclE06rcERZ==yyn zQqwtRlNz1fYUZS8r<0I{FYCM9cSf??u(S~tiNmDd^#NOB7NCQ5-Dhvq=);=b;-YF) z?k?7ZD|$NXw;nZZJ!0Cj->5a(qwZ^OZFErhc%ASEFX4T+%Y$9_ShyPLbc18`#(?TkA20ztX-E*!UCxQP z`bUhv;xEDoSqy{1-bqbNn@)p58JvPrf}F{YDfD`Gl{^+c(|8;OttWfGVq8b_%~ zwX<=j>f&>zzw--o2RAudH}YV`D=>L7wP1CH5Mj)}uFnd5!z9X4kY=iZvzrdM5sU5oV^0 zfKI_Em-g)XZDjawHU@mC@hsMqm>X+kY}BX@n>2Yc-bTbW+@Da7=ekN1p!uU8{YVXV zrb-hvuqH1{mMgXeuzoRpn0}h z2DHVxhTO%p$pII525U-mG_p1ht=pq{BYcYGIKUPhBq}dyP~}d7IJE+1{A*)P=#PmJ z0z!6xK4At53#t@8hr&;2yKVcn1;y@mSH_sznQt)k2{i~huzK&e1Ha{Sz;;=isemHBOw+5(J4L05`u81nsL=?V_UeR1-5g zy3%0*HeOlYnOT{*_E51_2CXzYW@dQA#NdFjRPzbV+LQ`5tk)yGsuISf27xTxrwl1+-y=d~0lSju^O&DG7U~_ilxV00^Cs}Jh z-u7G~)n4#f3B@3 zUkVd|>2bh_5Cq}@)5F=qL6#C`hgbn|%)vQzaAae}kR6y9krX(=uw^pQSXxBz6|qSm zQCKJ<20JJbEC`)ryANDHl5y>DM#-_%`xiIYo(sr~35eUjE$ea`abKaKJ8|Ogi<(nb z^nVwU2h>!@$AulwjUSkK>BrSoyDQ3buU)IaWyd42Ac==j|FvIg%Ra2xHTjibO(Vl$ z3tiK^=2tD8v&Y0(iNYb-v%3vz`un~Aj9qhx*3bms!4*4oZbZ(VGe;Emn3ov7?QMHq zKd5Roz;o>N*Au3uysa8o^NTOOz*_-Qc31QC@^Ue<&FSY*+TYRpYjc_sTe{LjeC^0r{+aib^DGRnN zp0xnC2%Gyw9NedUb$44j{rL{6M$5^1RPBW4M?xs4r;1kNd z8x7AKVSK^-(D7(iFrY=O@Lge(G1BCux&BTg(TC3GV=g%lwYa72z4tTwVOjr&^H;z6)z5zRv!DFrCy)_-NH7}}i0uF1gAd+#;|(r# zxPq4EFfk6KHm!)u!naOv7v@q7MwmH8#Ob@*ckbeaK696Px-YIz@Va?!L(#d2^yHJp zh55O;IV^?8^OKw$R$ekR_1xdjotfb?I&h2Tg_D6p?%%1+%FIelO|7e|L;Mk7#$D8* z?JLlx4{LV)5jw}3+S4W;Uf_Cp(ac=;xluD*R>{DSLZ=vv0ylgR75?k3o9%`UeXJyk zH3n%8Ou zBYnB#M=%9>utFU`Qu`=r^iHf_SU?|RP0tnDqb|Q?pVrUbZQzs8b=U{7I7nX(-0M$C zsx2LSh0OV(gAXp-=pTqa3Xj+qwa-0pty4v;BW*s1}$@`Y>~wAW%=l*NvfCLZ$TYM;Lbc1;HdorVS-LGTxZb&u_um^KZv zvu)7_x?~FmI;OkKjbqF-4oQ-8Q_#?e@R?vgA?Wf(@+O+Cv}nVaj}L1t{h|*iUwvm% z{X5Q&-gVph`N;3Cs7+dB4g0WWms7Zh71Y3*d@xOIshhntaiyO330sr&;rogX_(iOD ztaKWGds15#-nHXxXN?d;F?A$5d-@AI-hbftLCGe!=!@IV2?I6?A1!EkYZ_Q{ihfTQ z7-$cYtV|!hB-L`}5hJ~)b^v9AO6_YXw7>l2FF}^?z4snW7>tPai@_4+zyZVr7(q|= zGeeYMq<}+$Ch^ixALvO#@neZBuKi=cRIwI`OI>lkqe7!^W9($Bi{umgDw0BykL=js zvp6+sRpxG|gv`B>DUl5gHP@@1N(x;wF1jW7Z-|;6xM9fUyLC+^B_+XIwk5@61O^5L z_y;^EV9@>*dcC9%Yj#c2&aftfa_aafu;xP7tR?gJ&7bYBrQIn&96VSz;pD6%hqpa* zKWz!y0)aJ$Ca;${vXH^Vj&xVpKhtd|KbROjWM`^0R|9LZLWOzWhBZI?+;E1mW8;)* zk6fo8bTzSkvPhX1qJr$TKxtUZo-Eaul}e?+N5BfkOI#kFUY@h`ZPVYfzua$(3~P#Y zNl}K&{+WnacGDR)Tr+>$5P!GSloXat;IoKNPIN4Z8qyLc9ryIsa(C;|Dc|>_k3Ld` zo+{Sq)_{RwiS8aO=$sQM30oB3hlt7cgjN2kn3J)eVb{bs3X{cP3~#NfsuJ-fSd$ng zd8`SeXXR%5)o;84=4#B_^0%9&H<=xS__svA1NXXoO%>0Xwy*TsAfjl_pU0MIE(qm< z5GM#jA4hE(Wn_$ujMV=6>n>hXBDLB*%qG*0(9De740H)1#->c2;(X54CTEo7FHaQ` zJ59r)wm^(^%#G)%V1 zADMXV+`Pc~cE{`;8=dSgJ51c*Xf@eNluZWO22t+`f$v^h_J^buj=>kh&kJUu!bb}q z^_v=Q=Hh19adKSM$BEWJ$%f+bUYWyO_Ol(g>h092i9rYL8Wc!GCQfq^Uy@djg8(zu zYvPk3l_5e0J0-E3*frp6Ffqu!{FM!&tt+R$|U`?zqU>&gLXP%3n&9au(PGh*HcBDfK}I)9z?$vtnt&BD<3MDnjg%c`VHNQ=+w%9Pj?_|Q zT@Xap+mjQ0eVv2eRz9=J%gbRg2lnqjbSH0MMd*;)@Ltca$#WtYdN-0m4XjCiO)w%H zQ$9wP@hRrlP^3@izp0Pq608YM!uFzDueR$0`3V z!*#8;8mtFvI_phtUtf)qR~&0fEG>tYmlLKrOAuNGA)~l>baOMFbi>NZQCqi4h7RrG zl_gRWXVa;-r+YSdC#5A}qTaQ0m+PLHW@jwT;w`0eIgTU}oxHy~-+s@vwEx8Pe)fTX zw(7&0o$<8SqFjBfsV%i!ZZ)OVWpvfJ?7H0H%WO<-%seudjfoy-<2FKcXOrA}%r`T_ z-hUK~5B+bcOUG`!cr@vuZm{sNdvpJ(5ymd_4ZBX~r+ny=%yEjZmH&17br+@^=qqw; z8^CxcK;;Yv1!#{k(UEh? zb@%08QvE)x*)`!i$C|X&wlbTs*7uOD&G`1{MZRl75?N}EBE{+lc~RO{hkQD6x(Z>4 zy^A#wuAhGT>GN9*q`-)~i#5^t@MtrQAKNuQ8+O#juF!ts^(nJs=1p7U4AumcfFR}3 zA)ErR0;Z9tFz1>Q?UdA13rkCyP=O_TdJ-KgErX}V9EApQKQ&6WYcxbHs*WozDx&|y zvx!l6vj-90z}8LE>||DFxfW`k^7V-U>Rq78Cr}(zN}mq;VArgztp#g>%i)G%u614< zcG+Rx_0Ia^zHnrGLtstvu~UuCQ64HQE5%`q6N-J9MBK?$>JzEi*$Brlz|t-rNp?Fk zbMy2~DW_eI8c2JGy}sgDlYNbJ&z;j2gkwV6QG5OR#z&97W;ObQ2cyHn2nUl-^ickMu%{a-kg+WX(LT-WeRmla2y@+a+4^V?JR7&|XC9Q9q@F2_Qr69LC! zEzyR(_q@lBZiQWg5G=xY(2a4VfGNR_7#;D3!5k<76p|N{Iz(O~vcZ(#QCJq2Xu?6d zO%AG|7?TuG-ZpEi03e zLGBjAdm8&9>z~PkN1)dewku(j-Nl+<5xm|!u;@cKUSK@_=A>D-=g!}^(9VSod)f}- ztYvS+iD)^ap?sl^L>lz>;nxN&S@^A$hSn3TDPKX!xy0C5TpMJ5*8j|@(`d2j>FM!T zGrzvFeejJiiJjuQcqLtRdFgoo&7NRQR#TCARD5h;BEbXlP_N6^p$YKx(xpoPBXL0+ z-wS?vv%X&RhNESXG+t)cq(0mkOxa=2fL-!>$g~L7gaJK^@{5k`i{(;>O}bn7iepWQ zwYAp8ixz^A+uGXH)C5(>iXod#O{4bgv0|nu56sn;8Z0m}O|a05HH<5cw;65|yfru^ zDb&8gVc11m>0=D9C)GLlnfOhXd*xD3 zuqIKw=F2U-QoUDiU!!lT&sJ4vq4M7m?z;CwVb}W)gWjoKq`%Ex;(B!Nf!mS*;o~LE zZ%+$1a`rS<48N9duHreY!(rU`l$4ZJ$6RL}F)^`wE^rD=iRuf2#D$1u2MEYt@E>en z9pm*GCF(qY|NH;=fR)|dU1U^ zTUN449WaXdle|bE(bI9qiZ90Ay14UFaah=`h+Q|r^6wPnT+NL*u=l|JZD&s(IkYJ2 z$jtKNtBRZMKe~6jF(v3$iQ^gJk6ut)_`t2^kCTJVou?WMdp=NH#c$Q^YJFIqTf^lDr>s-bzyNfjm zxMp%PhefQ3E^F>#VZUa~u<>n?+PpX>PlZ%C9b!!*4ZZv~9f&;h`@8j!ZemTTzFylR z^omgD^gRdm?MLNh>0jEVwDD(xb&s#(s@n8dX!!p7@AtYzQ=~j&OyOQieridyqE1IO zG&B%N3H@d8j!wh~P9<#>F2R%;+vL9l6^S9RxZ7MuJJ)4hU*+pdhb)}T%*-SxN^FS3 z9Y!oelM202pKvHf0_C7mlo0$-*k1{v=++A=bSwY!mByMZDcHLg2fBeEJZNdjDK2iP zuU|vXBSB!b(3psbVSavHVojq3#-!5JJ#M)F{C>xYj=REl1(XCiR635!nkX&|8lk11 z{-Hz8+v8H-7?G2ghlyol@Rsz5qqYMLyD^63X{y3`AJ&v{$mev@Bdn<*F4cMu z&vab_T~HTtO8%>A>3r++*Rwm`ZCd-Mf;lGZO*Dr`?>l(Uc9-yb&z85R?=*E@V)SHt zHu+ZFJ!g(H_x)n$FUzo=n_yz@(92j8un0@R-%a{t{Mw*HFd|@)z#&8ox-pUnX%0-} zFD6OAD6#Bf1?u2l7j2w4&^cgbND7*WCQ>fZ1ATJP3U3chDCCVqmpa{&`+fa}4S9R# z)*hW(e|V~Yj*Dx>tZ{i0>znH{i?eVCq4uv?wQies(Kcx@cFjl4EjMr8EV)$B^tGSx z2hX;DAI`1#blMJc=Q#$w1`@janbfP_hc&yVThFm3KnimacVhgp7)EXbxd{4!Yu8w#$32@F+M&H%?EZVHph|F-F0a|NyhfATerZPd?{}2 zpl^S8Sa^c{A^#wMx@GW_=vc&>{KSd?h*a`~__MI7P!lf|?k7wRWo2cw5}!~>LlrN* z(pXblTQenvg+XLHs&8%0s;orP?9I)!YHT#Vb7xF&u#Rrm(pS$yZ({8f+k#P^sa_6_ z4nF%=87G(-pD@#%{X}Il)z-`Y;}o>AqwkF2%<$><}a=I3q$ku@xFfC{pOMB^(SX~7dnnCwwInaeAx2vX2VV74guD- z!$;2l(-E&X(}OI|H#Riq<>%MMrM14XRQSDz@QIJ`&b-2Z&fIL~G*b8J!r3m~{MESJ zhc&zY4!wdk3E)8Jf;9;iL51o-+_I>`-BHAv+M|2A@R>;q(Cm^uJ&y|Az?#}p!!d^Q z6Bdq~K9bMSX4e$4rrbB-MNk&y7ol^pL#(NkuBWa}2WUy} z9dx{NeBrzW#NR9p2^d-vHn?)9X4s;xT6TS%w|K5Vlkdnax_%C7NTj-x^i1N-P0v|m zHe{VmPjs0(UBvoaL{nF8p>KZ+Lgq0#idd7YBnDqMg-za#1W77v7r!D7~ zmX_w^-4KCDhg4YYbSk0}0i} zJXl-M79~%Z&4~j?g|YjE-!FUA&n48%d1jB-p^GL4xV^`gnSs~GEj(jxJfhdICJ#*d z5#~Ot%YXN~-;uY7gqlD9`Okmx_#nk5G6gLc5ZRUwFDM8AD#mLPxCy3gGdHeTgH@7P zbIQS>9N0uL8?iiya=hn2Np69~l!cP`i*OgPYzR>TAEP-dRyn9P!)-P*ha!87;i$A-ub=HM{yw zVflg0A;ep&#_qf17B8+sAJ*)e*1du?8GMLXuqNUYb*JNyO+)=bwc$hZw%|CbQ#%{*rYaJ&)nCcooW7O6K|M zp6l%Pt;SII70b=7tvV;tvsVUc4KCXb&^NiZcf{z?#MXH13m#b2wrP+?kFh46Ww=`h zl9i-Zc}bbN$QG?E3mbT6$ta_ERynR56_;V{iDGXk%?WC z2wF_ibMsv9;J}&(51N{r12=ADBlvZ#t^X8+F9pFkHPvSO_AY$r zB&#>pF*{hWQ~dwzodtYT*Zcqfv+sU$ zfeo88m@(LfZDT+!R#!?}3KX}txV!6ciaQi5Ee!YJ4uc`XWjJGu!SLq)E_VwdP17We z+$1M2UghTI=H%YbvG;kN=bU4roI;|L2aME)Pt$(X3j3dH$VC06T&eW=+fihm?m*-nj1XV{1>JSTphZwDIRAUb%DS(zVO9pQlfs z=Gj5oxqUXQXm@UDm!r3?-`lux*EOGkSzFKlzvOENPyTi85=bhwtEQh?Zl4O)%q2&d zAl|KEz=8R#6Kh94ZmRXZ1T`|aF>MVZ7#X{c#G3qNJf!R0^-{_@RQd;~DjABY}YKQX?D9N_yWfm%vOQl-t6dUjXKMs{QLCS3u(<;wPIX5ea@`p;~AXs%iCt?6buJ| zCeoYBny^T?ruwf5?H+czt-Z5h$A(`0dl8gtVv6qQ&Q4-G8Yp9T@K$3)r8E98^5l`q9{$C zcO`k0I%&TC`s=o;9kyppjmB&L{vWj3J~wavdh}?I?%i2|Ys8f+EV0SfL9xr1SDQH# ztG;Og3385GRy}54eALdW&9=4(T^=5?B0OPx9hDwyk^J!>jdRer46=uT4gPa7&E6$UQ#(HVv?2$*Eth< zSlGB&v0{vV@wX8FgY}U~Ja8cs$=Tt889aE#K}l9VqPbv(5DbbDly;*)fdaUvXf4_0 zj%ntExzkeNe8&&N8LXSMa=b_uzd1`FWo-zOp3Y`J`~=1(=?vE=CMLzk#;N=@3ES#4 zJ>KH%jk6~&oV@<$^+l(b%-faLqfd{N&`pyop6vYk+N&3C?q9k4?;<_4A9mJ$(dSUc z+K2b=gUeD&YZHAOmWW_YQ&M{?ROuc4GgE_a&I(T()+nZm5p!d%tVwyr-cED~2iP_F zJv_n~-c9|mwVyd1G-+VXWlcX-#MsChCu%3(Z{A^Lr1>R7x8 zonf)0>^d>vo;@_>m)B|>DpY51*(h_TE_*Aye;$m-JoHjp>+YRXCXVa4D^2syBGs>x zRbhGB8nIx(f{y8QC|%jrFn$KpW~gTLf-wLh!+M%X$!tst49$)D&Wxw?h*xm>EM2m6 z<@%Ki7B7&p3DahxyTW3PUx`m?c@47CAZ4y6I*J_6x?bVeKe+p>&$YS*Nb4yYd(x;8R;XI#ww_~>16!OOzb?KIgtZ#*{aQ%bM7b;tJAqh|T$ zZIo;J0Y$u~z1sonSyQPBt{GDQ&&I5i6|(X4ssC%(pq zo+rCzfJ!s^YMriIBN8VE4;wm+A&`M1%XHB)@hK)A3E&}}w%?saHI|L)HhFl+<@sLcXZWld$a;t7rtsu-4E-4g+q;9&rEeh7llNly zjxh->Df7@lIwof7mx!=%^Ue>$J$}fX zkCgH8>VN(T(`r}Wyt(bnnI(Ac)~>}U5}J~tzI-{hfB)P;^HJiOU`_R7wIViD30)OY z>rB16mm3DPdOTzbRI2Ov1w|_9VKpp+N-!Ct0^M2!Ip1BLJ`t>GlOks#5$jo#LE}e? z#&?oyU8q-kRh{OeS_~O6)O2R!ZaTPYpSJoO?Z^H0__m!I-ZWTk{bC(nB@`ph)aiFH zw#oQlP>cp&f9DR*3`3c5Xv`E+*p+m?Ixa1?ik(LS` zB_fmdhiD(nlLQEXm^fs>M~p;F6=L!bFXBr1OR}&jP8hxdM{+Su97$m)Wf;G7=~5~$ zBnr7oq)Q^$Q@Jr1G(I7|@z}(X`%;dbJGOP(wn=|Z9ddS9zkLIT%pICCa{e^M+2P;s z9$#hWs%onrzSCX%PFwByKY!o(E3A3s=#etteQV;u<@pi2$(Sg;?(mWE$Vl^Y;|E{W zt^on}rUu@c9g#LVsZ;wJ*^NiJvL^j8#tErgl30^t-0nH8d}Q9O8D0f=5#=n03^SiK zc?QK-ilFL2Fbb$-lr`Difm&_wv?f>+xarrS>$XoDoha0xXC+J4#r(<}kGa?RnKjpq z9kO_O#fz!D^m8}W|=`-&{vW{RCV74aq049N&NDcI%nWjPKegGhv@=3kNrAAJe z8k`cMevC>cQpo8e>frzW_rF{=!lUM9sRrLxDeN41c#p4VuAk5xImZhj1M% zm%Ny6h+h#iByMqhjC3@-aD2m^Kqp*?F^D0q1g6XcrwO73C@fQ=+P2lSmjqhTL6^tJVwy0b(fY-8GC+w z@BMwZ9pAQY&(Bl;oZj_B_ny1^j9QQ~Zqn2RahKC-oSj?a)P|^^*_=sRq>c8$!~3`I z-2dm!&8Zs(eHk3%A7X(e#Xum(z9lP}l4bLuzNHn!?$_sqH6hD(3F@m8gMOPCKW1Qq zN?t}>vbnM*V8mPpspd$mNk+$1tui{Le?+*c#b3-X4|Jurn(mPI{IG~Mb4mmWS$zeTX-ulx76|NVEo>TDfUWyOlP88ZO+TvH>N(ZfeK zJdl`pt6Ae4O@rHn>WXoF-=I@P8XhlPe{6{=ZQ8cwCkV7PbzT=9;qA7YNqO zd(&zqpdFRhv0vwJ$Y@J5(cXVSJr_b=3YjF0h4r??~w$+7CMY^Fi|5&}Mb~ z^BPXAWRsxEDz!pGiv$+}Y9`u>Tni!14owp;pi^qxaNrWo#9yF?h7SJ;_{dUv*eoGS zIFhjzM98dA*byki(g}&el#F<3j_8>18RpBEk62IAlz^3%6PppHO%6CiD+h@JtP+pX zn&G{KJ29j3mj)155-{Ltu>(NlL#OZ{W^+;j7eUOKw-;LhO}Mt1(KTbp%ldrs}$ zsz=MzYR4AF{Fz?u;+}}L+7G*A7H;$K?)}@>|GwTX_2HXImp*PhHX<=DkBFbEjyAdg zi>CX7CY3WUtcld%W+g(W!__{%#+&$DS(EOQP!B2BB-Yeul)Za?x@6(gLk4}7_uQmj zQ3p&nLyr8^hOCKIj6o#kE(4XAA&3Dir^X0B2sPhgL}VAXW&JB(nBje8Hqc4zrr*H{ z>gaHTpU(BI<1Ly}=1NU!uD2&$z{N-e3$n^O4K61+L#v7nz|0h+%Ww%D`1L@SwcV=h zj;Xe%rs}@{T7^>4Cb9LMf9^=Nk5-u0mWU`5iDM+7#V1ekTVZWwB|Lmj_@{2(x>>VU zO%q=<{SlVe*Is+gPUYX`tm)UGL&f{|m0E4u_3P6&Zaj7JBpK9k#}0L7Cd;|kSg;`e z(aNKyA6fbo8P(sZBbr7izgMx*tL`i9y~2Ml()gGB35%+?XBNY;W5?)U=cUc_ui%^B ztj~WLsJq-|2ZA;2R0ws+=7}{`s<4*fi4U74K4@0!LcMCsYSbN2FC;EBr|hrm7jdn` zk1XW=&qvLtg*ELQTqCC|EjmwAk87LeT|cnLq7=B8M(jDg7gIA?(uiG}vB+Te0YLFz zGwYnD2B@VOlwQPN60Ze!k^(MAnk-D7m?4S6B#NE+;~Wr+33p=YpyHDt+zF~;!em&; zhCl?PV;`l-!f1lklL`o};)sVbS?CRW6Kx=&U;L%>8aiZX%8Jp$ua4}0V$i1Jo7Zjs zdHVI4GcKjhIx>6dwxy{HQx}few6W{oD_dPV8MPFq*XC<;blIlN2M@Ky2WvlUd$m}T z$&u^}n%6z(E!Ec2>)~=DVjaAjjIKsF`XG<2nX9kJOL{*ti^&utvh$_+_Gm+wSD`d?%u6EW=z0; z+b`v$q;#G>n|_X-HTz{4`!5#neD}S!gTISLZPtH5(=|Reqdyf(fssNDwmXPuKhekTdd!W&pJ$t zXqFUUn@yD|v6M!J>x4OihDLwg^hr8I`UOx@dO?;PP)}*uc+n=%cklu?`OBx-Q{x(5 zlI@d@8xmy<3^>Az03~Z1F%6spoaXdF5F~%GU-Fk42WVo>1WED9;F+O*VaTLq!!^fj zBvw{pMGnF`Im2bW`}Xd+rgyVVE!XW@*Q!aYK4<%NJ<(&}jvoik`LS>RzMVREOlh%b zPUQKc-ix#!c6#_`i(6N+_GZ(5J(8`1{w$s}IkH*z;F^~8PuZW=&S$U(Kl98p_AdEy zN#D7wNhxRC!+=T3Q;9XJR`r%xb3otEvGH4--PA^wV~~r`|J{cTS(D0%yCHjzPBQ_x zfFnkwBl4R1oc&uyFE%*obz~x->3w0g&(+z=K1~chHT{iS7Q40Q<~gzNtZILiDgLl*5DvY5j6gXx{;OD z4CLq(=cdiuU9J1!0(E{ZR6Ekk2%Z{z-MY7qS^3LG8S9eCZP z{nQ{jxvEh!2pa*6sZjU9=OeYBjnEbwaI`|lnGwySHAbM#igH53j@i%aJD(vV2U$_M zfK*C8LsL+a761b$n!NDB3pC4mhR(-_CNPMPXgyweBP&A zk&-qOr$2nm0gWL|7Cv;A6UOK62V1v~;tjH#FzCr`eN~IH(NFT zHV0nHJvS-+G6YTX!iKDgp$lJz1Rwf}K$FyJ{WAu~RBvmh`NOVl>iTXN>T`9DYIqxU zM2;>bgP-Fq7EOE1J~3Uk^*_z7QudqFKLd*W{rBIuZr#ES#Cg6>lc#?{uE&@{Dcb}# z5%Y)VOLqerm1aO-kEwwvYx^$i)w>s@h+Pv40fqqsYUC?aPkI+Hl(toug7njIHL6`c zd65>JAIkM+^e>P=(*fLXMR%xwpGJ-Y14CA>O#JIFb`0t^Y#4!GtCuZn_SawEYPCPy zxl?oY?2yKdjeINHDSBLNDh6unUzHUdSGh9d1ZFC1+p+yv%%I;2*4WlhS5LZuE;Yoh%%A^y5POTXA~Tv??!U235A^D)}bhCe9YZ$m)4QK600 zx>9K55o zDOau>i)-f3pWo8mn`z!zWs^P^oiW!@xiE`j)X4IO4;g|RoyL$R6sE-6j=u=EJC%#2 zI=KKVCc$7dfLsHWvcNkL(p-rv31bTLgj)A*)AW4HItS}V&x%dxUyH{feg0Nb`K|gU z5xeI7{7KsT4>Pse+g}XPe%Rq`$)>PobGfo6cmDCmA3MATIQs{k%bE}mow2U7lvtD6 z5*VOt*}QaItd;RmB7Ys0P`xd9%tuj8ckbND%*-UbmG3gwl7YBI zDWEW+NDS8T#SDn-Ar zZma5K^p5Ppu|+qAN8T}Hll`_Wx=$k)`}p{#rTJaIK78A@4&A#W+KdSkV*dGu4Js?% zxl?`4oT?KhWQCCD_5u`Wc1*A)31C8|Q^fRw8~v~2=l}S!+sO};zRQD~C8wKWvs_F2 zbu*h9ahbjXHek)1vQY25uS&V7d>id&qqUz7yZlYh*^w<1^0s4;UfL=)DDN|~HJhQg zTOCrZgOL?s^)J>xrbm$Ak{%XgA;bxKazL}jF}4Y~5bq)Xi`fyBXI>ac2}UGw=vK0H;zP%Q&hVJOIN3o-ESiud ztsKsGno3NeSWobz6TFW36XPf(x@qI4lmlZ1pC2~-)W}JjCXbyzwq4tH%-v*OY_-}6 zn^m2)rFz|at96!9Q>)EHLn)au%kt+322&MR`YnJ(L2|PVDSO*n!|>%wvjJuxt}|iwrf{ZWMrRSy?UQJ7ogP! zUB6yu?%c>OU2;v9l7Q4Ef=r|o$O6p&7?rU}GeamTu3iIg+eF>z?vOhvQKCd1MHXK- zQvt`YCYf7Z5qh*_@B76EuF|w^5fqo*7N9Et79D6b>PGG2`z=WvcD6>lH6byr%$>_X zLj>Hgy(H7jDRR89>q}-%Y?*){eF1(IfRecr^c?^wUTppn8cw^%F)vV+#r2re!;6tK ze{q-dT?myK>r@uLi;JJPq()MF7f8ip&JdkwP6!e@WHLL6K#+JO2sbA{1uG`C45KC^ zYQkE;O$K=Q+*x`Pb0$dIt$VjIQ^qvi-J-$iCN*}|YB0J{%VDjOlae7(66rr^;L*-A zwXu`$)f%tGtT}6swqWPX_gm~N-vrhSR|i_8uwyQU%grpY?sl1TS(D{U%7Gx2N29eLAA7|&_FXx-?wicYe3Sn z8~i$EOJWUi6*1PLEvESffZ%e>X^iP{Epa`rB{~CxFG_FY)K#Yir?8NF=YY0^h#9>m z`$-2c7i?qJR4CY33D9IM_W?(atUYunzHVL0`S@|;!hiq0+Tp|T0|wyv&i>Q}XDA5- zEdcih=XGyUlRJkp=?m!~38lq8&8XN`(4hMv0h*3%s~yIgf;*)b^CQ7Z+9Evc1Q)S*1+j)8EX+#7NZUmt2|H3586g9dEGz?hay_+%Rooe_ z!WTKv03+LN3+;nU?dYwrruO|dd%sVf6rLQTF>H19 zm2t{3dw4ps%**SyEbT!p%^}ifWRp{L3qv9XK)N*Z$eQ%u@)FnB-B`hzL5+i}A5N%w zvUZcR$sOl)3XKVEr)r#`98}o(5K-+&&BHf-n>k|!EiY{!_F#)dV&o04oH%jf@ZrPRBNm_; z(qvgBoJ36e;bNRtcx*Rs-ptQYk{RpMzUTCd@a3qWfvLgc8~E2Thk>YotU}^o-EGX8 zY*n9_82a#GJ*{@`kt6H&?4cV&rSC6a#@HEr=umXeo;k^-f1bNyN-RC4{z?7gf!|kM z`a_fSl*!D;qEn>j`}NmfCG)oaWqCZtb?)Ho@bI5=Bx_n-HX@>f7hUNy|Br5syFXF) z6Iz9hyilwCy3j@w{ez-3%_c_$gyzng_8VKmudI~x>8GF4$Wx){#Cb8g<}db6Y#H<~ z*gi2#GEU=wdJ2Pbl7TeU7Ce<$lfS&MXj0KQ$&0`tw#=aAT)u4i>a%MWUS2ri?4*g; zr;Iu`dgR`e$;&2>pEjO~NpL4F_*t`O?U}t?J8|=aaci`9?q$u#D%wr^eygqC$rHj7 zy;zl0HZNLW!VEmR4my!FD_8a%I&?Ur6J2?*nl;HtP(UUoG}GnD3f9zwXyOmltbL(= z>vL^8r*#d93az24Ixk?@;pVHB_nOx(zNGiY*;E~-%uZb_vDu|_ zm;3kb|2X(ZY|KQ^;u4ak5aB~-FhHDW_HXHOijVg1+rM$dnqXD#Q(Ps`#BO1_WZL{w z6Zfxs5X%ny`~pUe3eC)Hd*HyX0|zjQv{||Gy9W_3e~v*-6wSKmoG`+2i5_1o)M6XP_Ur9}lGd*b?MFmE_MJ8Yi6u9cwtSyQP{j+okZ zYv#x~mpU~Y^wz!}Z z`uBESPInu*C1u>A@trz$!qq_CXOzzCr>-NlpZ2(4DEaon?b@OvwI6l8SG46%n&hFO z4Tzo9uXN5mX0=h*sxP*`TW7ImOpN!a;YHHs*74O_1l~HS3e{B&M4>Y*@9FNBIh&mjzYGI*m6~fi)m6%ojUdB zpMT<4GWd0}FHTc+e%!RfqxZIK-U7ov!AY!v%vZP)w-lp%WHj+Bgh?=UsYAyOFsq5n z_=t2cZ#aefZO@v4ftAjjsi4)yUc9*O&>@1o+WqvC;@&+R>ybNl#Lu0ptWd$U8hnc5 z4{#)dvR{cAWKoHP1BL4BDpa$7cwJUzAtMO79iK9G){mq7ZDb`8plMD!Y4dC3&zcm- zE`1v9*G^dQSC7O#k4-R~_p7Mvx;%c+(fFu_xtlY&-hLKMi}Ho}LsBiHS>r{ILz_S= zN*%<*4u&$!hA$ac;}!=W8PfA&;LT{TZ=b${7Yu26p>6Fm_2W<0jQS;}&aiql+SOnh z6ApUZ^u&NNKA+y@#|Nc)K77CVpZ_oRYNHtsKJ0wEcuUqaZ5vokCZx%Od+1~!gRY}m7*cc0!^D49@2BS@T(6pbXIXHLn|q!R9li=0i*@hBPmG|L_h3h6a3 zeDbooX&Lj=u`x?)R}o52g7}YUhz-7?|CT1ZU;lo$Z{N-y2C9FVbc}h!DWFN2w>fJn z8#eUPYAb2A9q!&;@!M~kcJJ;rbZG4D+eC0CtXL7lX6XOTGR&@n?hJhuBQb+oBYjE{ z8B`(m>U+)h7pc>yL38>)yndtm4gO8h>2k4F9m_}R60z=p%oU)i-xxab*duu9F-Lsv z%}@}N?;{3Ark~6>eCqI&8B++RL}X$v84EFClYk^ll~^J{Lk8uX0qy~JUbHMys*KcG z2mbV>)ARSwkFH}le;L;7AJBbjaLUR~3rCI^$>5%!;0|#!F;XWr({~%7^k1B^VE@j2 zm_kiF1c?w7Xy(iSb2PN{8k@5wJ7NW1zpmD5yWG4vdHeQ^MT;6;yH+7HGvLaVnmEwS z3#g?(!*-IB!of=aonN;(_<4)2ox3ndV3;v0wAqPw8yqcMuTz;k?P|?0e(=EumTD-j zx0M1e!J4#EQ8B@@mW=zp%nv52c1|Br<78FF%wf%(o`w2CbE=F~#w?FZSWvrOm-;k} zw6_2#t?Q%M&a9o*lbbd@9z9fByu0>;R)4(HU|LvmyM#vg)>*{E(v?GUx)ZCEj~QS>vQneSOsA=Ax&$vP@7)ftYKh8=MSG7^qS|>2Oi2D=uBy^u~?kHf@NTfsOROnAbzH zgN4?NJ+Um#aeEd$A$w#GA3B^sJ$C(J%8mihGoGekGnr0amy=nV&`{fCDS zSR6TfLXD9oyzD%$(`TR9o;CgaG)tFO)@nl@J{7-Jn4)Y0MQY{wHfrAF6{m|fG;kv&ROlVss%*3}$e?)e`IrQLY%z4v65y?J3x-E%1`E8zzs$2dbh;>DBFw&47s1+E$! zGqP$+?Pq;i!u{5}iBlq)whxMD#LrYDWDE`r_73;0RMp!z*hUdBy4%X_fcq4n`9x`S z3Tu*0v9aD`M}5$zXDL6$;~vu@)|5#|$`fju@y3R%No+OsjahyaYpLY>sjIybe*M+R zyC23~32ioC9bCnrMtE1KkUno7TZ3Vo!&PDMXyNvQweKm*p~9@t7`c+~;h7jNFVYZn2UuqO64 z4At1uOy6plI&@a(gy|t;h6VNWS3S8fir%w19W>G0_N?ja>$PzsaH-U4dmlM6W%usD z2M;iFh8;axGc7HsK?6Mjay+DeVr-D($B#ZBH^~dyMp$!J>g>CpbiGhGv2KOjr(xxk ztN=|jnnwFx;`Xe`#Blp6XnE>3Va=TCl2|M;aNX9_W3@bp>d*-MMFSd-G==cm>;YI4R*)|93@WPcWyIAdGZ zBsh`{XLZ46zUo@fhu?lB?C&Bm=R;e}_m8IceZro{m8{{y+z5QNP?4^>=H-CwV4+}{ zi*Fj>j6Oco`Prj#@7}$vf@%J&Ad+H9_M10v{jYkjqZI%cF2VLO+ScH&2u^F=cih{7|>)g zP?f*_iobPBq18qathsY%456-kn(IrcOo*@Er+ZJ8x2cUifAH}hH*E@06Ie%>rZ70M z&AjqWysg?ai0lyN4LLLp?#-HSzx}oxL^ikw$FL@q-bbxm{#Ct^s@idy5M75yn_uv! z(goL+emkS&YZE_xvM0>7rLg`2+CApr8%QDEI(gq9gZwmyHEFw9Nx_LxXn8C&wq;GC zHkrO4?b(f(N|k?+;O#F)-bo&GS<^aQ9cbwFgEg5~i%}D@Av(}Ne=FXS9XM{}8KbG6 zQ&zAh3sN$_hfT4Wgl;BlvdS?LBYcGof%fj%+x1Y78dqvp`LQZSGCs;KZ|jYC z1Oo=p7RxA08Jk8lc9Bc#o;u5a{D9!@MptpSVlQKTp=@|;`a!a*k^33!_g%Vp-MGP2 zbgzdGrFlZWfBX@@U_n^hwz+RL=BLnfObWZWBK(i#{u{F`)bQWW2P-ujzo>Wmi{_!4 z5az~WrA|stvZDNu9vKP*$iH^jFwwD0?WS6H~EWi>gT>lh2HnAJfx?1{`u$SnH^5PD<|4Dvu9?mtSQ-STo*LSY}>LX zmN0dIreBBNz62O%4cwI8s<7D3OVd{FnKpBFrGRR_fvlaO8|kSe)?~2>0t(sH#=0I< zmvLlA;lhPoqARuHN!dLTANudV|Gs0#4jOdbQyUzRGN0lBXNO-Po@tV-tO+}Un^$jK zO_@7dso@eO3+Uis*T*mRmh2zaV{787p0&Gn?~yeFos^k%gsKKVq5n3jO4J`${}|C@ zScJdfK!VEvO(!=fdOxrKu=!_JVSjIsrmfBn|R(gjw2oj7|!sn;lNRG1p>Ew#IS^vN4gVwq;FkFYh6mP8))zEsLAnsYWZmM{7rP z@7{Isk|kf&-Sb*C?PqmP2iEM%l1Wkpl30`3;!KZVHlGQE%C*Ar-6BPbn0QiIeI$`w zt`DVu0UW<`=guQXj_~Q!v-Jb1F>|MRvV53eaDWftniidVBlY&d=&w62CMEY_rJeJd2?}qc7tZm zWL36>QIK`VC-wFfsbROn|%55 zIZ{JQf6Ixi=@+UjxW3$*Kb1^a)2!%*if?60GbA`l_W8dC>JP#F}!L z?vVGAVO!P=^72p93|r)%@>AI8$%(_FgQMu=m@PDT^nz!CZ@n6EyLh!z!A%#aga6(A zTw+Z!o%zlLZd$!61Q_93%Z8Z%I0I*fnu*>&}xDQ-!d>v*QH;4A?SJ zRr-{Vc)NZ3c9w7lH`xijN|ndcv-lMBBv_WOvj%$$|0KdLCw@EZ(%Fmb2+XIvnApyi z=(^uBIEdH8F6lUb=d$f=4X6JR*-@xap}ZGP+ooU_*5t9{;)2ee4SlpRdesvr5?BUb zZ|^aES)MpOT?uPW__p@V_uKsTeuLRx*Ese@ z-CqmWp7TkpMXX6=i=3@Zw#{W%NzKhz6VQ~u)!d1!sSHpSOZzVW@~@QR!Yl5K$iKAI zJ43!w8;;O*^**l$gh# zS`ZQogQrYLd)EKPt6?`juYNS7MY?|s%ZX>XXeHKU(nGzvb$9LFUE#a$tf*u#gv(8L z0OPE9(iy2UPMtlqZ{IJ{nwNy%;VYN+w~$sT(oYtPX5+?<@uU+A%i@~s>jR`3Kq1!? zz(DvWu6BQ=|IF}l3qxm~Ja&?=ka650Yi+VYrGB!y+vWkLgs034pE5kKzn|)Ht4W^9 zlWgq8nhJ$~Vq)as!!d^s$BrHy93P*1q^3&cziPbijrpNBcJz99O?m%kpR03yRt?Bn z4Aqy9i@p|9G`k=_WE5LTDNJ<1gvS( zreO~TCUk~OT?)v8CmjqWzVq>)&O-!2i_ zgVi5wK6mf0{@Uvi+QWfo7O1LKF&c+i*Ei_kJ9YOLs(rFpyZaw??Dj)Mj^VmbJ_e2g z*-q4Okp|LUx9K*lNws?O%{T2OlTFig7HcXss?R5UpD*ppz=d&RCyu>*>q@|iYOhZG z#OP$oCu^|9@8}Nx_rL$yR5mwFOy;bKt*ChM;_jrq4OvrKy(lO!Xi#v6PH`>OS>0w< z5FxDDym|8$&6*_j$WTWosRJHwcP6oBC57U<6DP`QwG|E=P#N18+C+tO(PhIdW=(}E zct%*nhAM6Lw>xm|;IU)J;^N|{EJo9z^@lQ98WSGfym>R=c;?I*f;I858@(QRVMHc4 z6Ta!A@L4!_!G$B|Dpsz@c_@=1gi+Sy)^HEG^od+jw>$rop_rXoc7QTliJmy~Ll?xjVa zy*cHJN?}IUp3~J|D-MX3k>Q5SGA$l*W=#n+U3%7e_FT^+YcewPd&C*Bv9YYhh*y^y z!bu;Mres9M578+~|8Q87sKG5;wlr?sh#4&URY+pZit+JfuU%tf^z!G;DfPcdx52VgL)#2m5;tc>?ewnQvbM12Bpa`1-w&PpA2V+3 z->3dsSYuY_khWaO&&e4yDfx0?O@?48?Ns-dsqQU{>D~6;-Ma_1e^$IP&-==3zd2ou zvL-n_tYX67#kvgrzN(qA7c`_VzW8GH`Yz7ML;*KoO$IZr<*RiTYf@)Rj;r|gs;>)= z{buUC>BCQrdui2|`DT@<6lzP>6fv5XRc+a_CLI+UA-EZ6k~eu_%^+3a(4fvK;XUfb z)n;M}p@A6iAK{C4!SIu>MrVFd{c2FdN$uOW1AUe+U!HxI$-k^wxpJ8kCt%GAJ9enN zyz~Jmr(;0VRU9`)D}cqUsZfQ?3a@#-&Z2uuPFy`Xc=BKx`t0Ixl3mf+_(w{A-MV$N zXV1oazWwNqUw-{X?Vn4SpO2r8~1C$;&eazS~fF?fyx$4g-l72K&8NPekmMgzs zjtmTEn@WCH=AcO~$b~f(%7AT?{cbJrx;V4mfKC_ApFgL)=ksv8;?`ot@D?i83N!ZN zY@8z4-&jCr_RERNeO@f8XSCwU=A^5$CQY}?t*7HG)})5|g)2WB^L_P{`g`{7Idt>L zN1J|ld-hjq+o2{^-sQ5x$#qeltclI0V8Mb;mc~9mZ#8SOO3qkKkJUkw*43Pu-hW1f zCKP9@G?84b{PMN1KVJ>M`EJaWkfyUb^%)CmVq*gk4f;hIEIjY^>(#3_Y0`H`kNQlR zlKWyYkBn_D5sGdi$1G+|7UGUpS6`p-^Og1IE}Tnjnuxo^Kuq2a9Xb>(rNldQ=)jif zY{L8F-XRG)>a^d`F}78lCg8~}>7PsjSNOod9)w`7?Y!jBp+lugmEv>KF))Z$R(Vg4 z9J?TV#*~)hynV?-nN9{W&|faBsZ@rh{1~M@5`2Hlu+)j0Hf_3n_g>1?3$-$@1a?R= zKjQPrtb6>9wNAt~AqH75!?!NYnsn2y7@pxY)>NvzqJ4{XEZty0lO;=*oH>1F{fW)r zc2z&}2tKa4Rv)olp8!pZ>$rSa)5k|$r%pYx-;F_&?6I0PLsdaPsYhjKMl7o`VOG+Z z7Ew(ev!?3X*FrD99CoW*(uKfgvpV(}i4T4K`t{jYg~Seu?Tt^fy4a8qHa5w%zF};0 zgOviUU`>=^f&3ApMzDYpv6u!%VmO|~@L8T+N}1_CEZ)wxpYxW@Q!e!nUKSRe9@B7C zqma1A!LmlBZV*ssLG+AWT{irE{YFxwM6O}KO2TdRZ&(u?Fg)+p7VeejvZ z_1qa>kPB;~sh4-PpV!A8J+NZY;@!Jr?Y|9ReZ)hEXTjCn5YI@eVR zeRRw5X^njVO|p6se_OR`rTd)J^g)x+QRmK`*`1QOg^cAHigb0rg5a3+xWq}x(MeTJ zTh1_7w~TDsBcjv#220lWT?yH<`X<>&n$pizs$A*(rSr3*Cu08W8rXhJ@L=x`D(ap~ z&IfW~O(n}{1_n&-qlyUMw{IUih~9trAXR%znYmJRVx}r8m%)H8)3YB4i>925pTWJk zC~Gp9ab2=;4r?k^-?j37dqe4bX(gJ@X?ys$-|B2=_G(6nHzs_hshGQMC3`j3KZEQ1 znP;9cC|csJSqj*nHGk;SyH9p!u73XEq@A0ZZ~mP86UMwdFZI2e)n7L2)?M?Wys&13 zDs)Tu>@^KjM>QH$u~NmUQ>W@J#?DrVWmJl+ldvX>kVB$S3!vlIsL{8XnH7#7uNfat zWFbCuGg*_w-NsX3HEXgA!i^g@=FFJ`qf3Prgktj^{ryzgI*8cWYSpT-qtC!W1CtLn zuYR(2lRe4Jr?m{O68vOQH}cL~=`%iT_}s8bi(1ZOO{K5C`ifnh5TAQy$@d>y4p{5I z@YgXr(<7#>icKHwKgd-IzFb%{{|}mnt5umRx^KyVHQ9G`$DUm!?q)DI$osd6%D8YN zSz^X1W(K$}1h`5Ovf+KY3~PG(j2}OqVGL!^)yc*=tchzhR`J%hviX1dV%o+ThYubu zyFv5zvajBp_IVyzli(#A7D$gv%HFKmI(bd6_9xo5n5fa@xEsnN*36$aJL})Zbp<-M zd)+@*JdDaXPERIjI=D*z|^V2tO+=zjV<;RKIRFx}N{?YPAKm7f-_xtZt zThi0h&0$Sg=jE5(2l}mMO`3ZmDHkkQ0KSt5{4{*{aMm``C5R;wqbAF1`fMMm3J?0X z=2-Hd08N)`qFsVD3EF8trcT5}^U z@6BXQHv>&F&_vnONow=}lpHs9-1241XU?1nqq8Vx)v8rFDP4@(#Q(wfyx1iuEiH{$ zz{{5}`&RNbx=6V3pHL}!26kdL`ofshWij&>)J|PAf6*ChrjvX-`Ib1#)=gyXz?CCd^mT*`da^-4_QA;mBUh2 zMsrF!$?)U3=bp2#b|Q6KDkUwR=f57c4Oy{>(Q%6L_~xxDOifYO16|_ zi*sy{>=?9c*)o=JhdwREER9+-C2sWWu!%E6#$_bTT$QwNdCj!>QB%_*rp^c%%dDW( z(D4~j)8BZ(l}!GYieS*b+w85Ecg&I{k$+trm70px8B&Bb*Z=%;`sy_en?5lI%bZ_i zW`OCECO(y?0xrOs3gzTEU3P27%)Q)Y;^9_ferYC8CC$GpXR)RtSo!h9@+_$NREIZD z{C=|Ww$6noeN(D_R#<|iFIxN0KmXiCSCuwDryOym{aBM(yfIbUc5J?SeI5xRmRd&|GLEguG{oy;(CfG<4OfRqU78y<3+){fDUh!<8XH-qWTj`t?KbTv^jy zC)w!$(XzG%t61B>u%DyCH&twBkq2Nuad0tgJiPYzNT5zT|pD=tz=DwO4G+ba7k$Fj_TWwZNKr)4PV9M?c+gA z$R5Fo;Oy0_*Y#W1C(Idt>eQ*q6{Q9C4GMcMZ&^INTS$k6kuzt7j)yX(f763U1D$ii zCqDhu)Blwqm-$~VtQi#GziDi%Giw^wZ$Ka{E#AdT7o&emRo-8sI6G4nYu4N#%8vj| zmnnJ1Zqy}M^UE(wwjB^Nd{vV!>D50d>GCT~JBu~_swj)BEuVk!SM?S(OJ3Qw(E9TE z)4u#7$uicY$`>tKR9G~PRVJ&>*_AcvU9n{I~5q%^gEs6;v@SQF56<0v#MSyQRfH21H%r&{u#&9D7^?b^+2{*R`( zks64}lpt0nVkQ<7g9Z&8)a$pt)%Vt#v|(~&{m5LnGKY`xr1y>JHaNTwcFl~)=__N? zSH#Z$Iet;ZCqYlSU|@+XSd;C}_l{LQSnBt1S?g|{S%jfs!-hxCob-9P%IjjP_ns6p z*E|vdTNbr)GQ3#+tjSgmu09#aAm5$EnpnBYxA%H$QK{#~d|W#%`JKmJ&Nra^}0>8-t4GpD@7b?!Fn=~$Ejwq?!AUQd|N@ZJs{Jh)Y>mQ`wY zcq#1Wt6?{a#U2Z8mf>Hup_CChu_jAwxG`vwK~}ORgC!qj_00+GZg#qN@7{?MC+5zb zi-zp$!@MiXFgVqwO`G`m_ZD_Q1-~njqmDtC-OZ*qn z|L9Rl1bgp?41!tLodg?@vpX^!e*}z=KVSgOhTdhfbXm zpsCAK*FBIqYl3C&M>bAlO@*pl7q7RLef`#~68U~AnQ!J7#fFwu|2v=4XrpU*5{stm z7Im54xl>q^-ulHCUzDjLcD^l}uqG=XB`9N;G@sfsqOpE0VTv?eY5Vr=<7?DhB~NBGAMJ?)4SgKD6P{+&~!ftFu&rs&6TocP0E=2Su~|$<22UvQe`C~N3RW< zGNsOtPVp@zkm7HP7R-NW4ggiJUfoAs<-@9*{1QfL!s2y2nw>R00W_&^d1OrlWRFVL zGiu+n!+*hXU?1 zO07}HEnil3)zVRehp<~c>&_lIa%A@G*;6M?_49fheVaX}C^H{@^pV?Shm3k1XWWWB zSrgFwG%3^S4x8L$IP|Gf`Bhf>O`bCSn=ea9tcm-Ve%7D| z)n`qbIi@?fx9eP&BY9^{m7l7~zN8wvYOOu6F0oD0!i5U~7dD~UzIA(#?mce&b))m( z&T+%zW1GaWlCX6zKvQ?aZg3zI*38O_@|FEQuP|Z!_};yHcj(aJ;Gsi(b}g#%_gdA! z<|@BOwI^GKQDEMC?>#rj1@Q=y0y1PxT41&%bK}8`6IqkquR=2)cB*`P2C(L;;E8FC#!gQj4QPHEobrFM z+JJ5s)HR26=+F_nreRHb^xoUYCp{yBSy{aEsO-oqYbsO$Lm!1>uCKA@_dWAg&kNK9 zu3x|Yz=;DjrX{RCyC!`}dRA33&Mii>!tf-kxVcCXHupAV!J0~?x>l_J$+S|}7SEhM zqeqV(>;k&}+(GYqi~MdcP^5KNF?lH4i|7c^>zJF(E^?g=Srdz3CZ90=RsniNp z$w3th&HP%arq9e)$+Mk!=bd+~d`|H-a};nGYkCyfm{30NtVtaSQ3Xx%@4GE-?(`-p ziB%h^l%-z}yYgDZop)oe1}D$z)Mt!w*3@Wz*s|rbyLWwCwD>+U(!@==-$!|6O+S@p zbO7O)v3skh{xW;b!8P6ue2$$tHemOl(6teR4h`-&qf=P*ux!njxhLGrqA8UhS+FJ~ zd3B!R_QKM)=Jy!XzgxF%W5iHGkjB8seBE4Apx51SN(Y+S+OQ%?9)#_ zbxWP~AIizBiC@o8r8aM0N)KETM?a?=aqJk@EK{b8BOb;Te#0iL3BpuX`jl2wsI2g2 z$fj16c`fwnD`7W^S3eQbd~TY{$Hkh+44h9oDvO zTUM=Fxnf28=`(AdJ+6t4*3Txgqj2HE?vw@MS#%1>gf%?}G*MputVzEhs=Tg_*!RHR ztm#hkYHgAe_Fzr=OQp)EOxQ5L_yK-_F?E~HimWkMp;Ea?aga3|JM34v%u3m=teLe^;8X6fQlME}zc zMnCwh#_vJ(MyvdTb#Mc0G3W8k=;)H$wtX>W%8MU;WZwO{$yK&t%|~jgL)9^>swLfO z-ez^X+Fj~sLNo!v0Xm({KX7l*q}<4bH7ojR){Y3izO={U=?Mu5D>tl-zO%yb?jptZ zQ7X;93pIEe1h`49w&XFK&YC_xKKb(Hv*eat{#rS+CKF^mEjyfCp6tY$bnU{TX*6r? z$(kx3b?q0!Z@wCS>%*Ayq0Q!NA`%|0e1v63;r4SLjjF9XX+ze;80KHqKdfnZSj}+O zZZsb*byw2sc)}`fvS3ZZF?%KX-CE##D`VK9HCQs1Y+dhlZ&~FFGkjJLkXV!ZEKs0; zuBeCuw^YEntVxykz}Y9VWlhiAAKS_G+8(SaK+{B~+LbkZ)p4(fo@bKK$MNUFTF&>6 zW@}0|r1vbxjZyJ!$(lN5wCVud@sJ21S+J&3>7Q6nb8ofxA9F_@UKbh~I%|Gf?JEZZ z?ypy@8_I%5++@Lm1&x*&anhX>a3X6`)$(tf^FKiUuuyE$ZgCjeiSnJY3};1Y?Pi6f#aGtm#oEG+D4F;{k7<_*)mkwFf&K zTHdsAV*fRBga6rD{q7%u{W>XpeFbRRq*;@HBFYJq2hCPhMqtcgYQrI%ike{prg zJG3op5?}w>XP>#DOtyO@yRzmZLz|yMQ%x06Ri(~y?-nUi#CG@Y!YRptH8C)I)OcDh ztchuJL|1j@Dn;hu%=QOv21q0 zgY~M1OH~h+Rn0sd{BWz{&LZ!tb6)#iIAQc_i8~)Vm^GOUw{l@kwyN{-3%#&f zeS4YTjdZ_D=`sHtt8)KX(d24RKkq?H%z8qRGx3a$WKF94E3dp_;$}SLBbl(~ci(;Y z{PWLyi01Nr%)GNEz4%8Swpe3cveVwINe3?`17*)3XR#)|j>iUU$%i!wD+wDrG~(gG zD%xY3+iQHT&HLfEanHTwxq)=qD~Q{laTsfQdwak5;)`ytr#TNO2iC-*>482ab8^Sg z*W{5kJ!GXo8|I2#S(Dy9fByV7yj!V z-mLlUx8Hh#j21nk6Im0`wCGmF#dcS~I@ZL5_V(Lv+x_O;@dxa|njSm*#QXx{NY=!> zAsjbkjZ9cmOhGljnz-?mc4f_S<;n?*X7VvzP!ke?AhVuGgh!BWFuDIw>sNw z&YG-GE666->~I)sQaeO;NZH|Z)?^_ztWCm?mTT?sgbVD%nv}6JWy%PvxzQrzc-AC4 z%9brFvcqV0IQgWdtjTl-&s@O8R{3OG*8Jgz9|ZTClQoWEO}PJs7hW*uemvnC=dvcf z4(1KP{hmVlb1F{*GtOk3arcWJgZf;pFocu_mA?f&%lHVYXsTnmIsI zaKD+^VQTKRSHOJM6r0xDB(rSAns^Gt0M7gzu{Ude^UXKr-;9S`=|t89G(ALQ@t9^R zU~|?iS+bTj%3YGKmFA7&4^E(tbm!U$-G+O9JEPR*@QJQp^5RdMcH9j)+Aud zqMPxMi=D-qOw{lYg~el`R$BtjVl%;f=CY zPUf98S-?errlr|oKh`8DP?$H2W`|Q)lfV+AHzQ7ZxB@n0O#zx_mLlo!^-IVe_hur51n&YC3n#TQ>Z_0&_=-H#`H z%aN=}c6{>5C!V0Hct-0K$UAE?`-Cbl#+K&mZ&%jD-TMCf?~4}L{OoY@l_ppdU#(bA z$L85%9c$9eiDkp`k{7mRO;Y>(^UsBO!*q@~j5W!Q;>C-bz8UeUhbxdr)}#fd$_vo6 zs1(_cHL3CfG%d;t=Uwj}@#bfu@*so|o*fEo%zUG$$_{!J=7E!Tq+%kvy^{*&)mu zmga^1Sd%HJ0yHhn4iCTHQr4vO7AjQ8!$r&Ef`4m+_Xpjo(ZVJq%Oe8rO$u!uDUXxgNx*orl2 zf!}=dO`F`3NW^IhNUSMV3~^WnRgG?O*Gy}gC&+*VnTch;<2xiUSbt?oyp<4gsZ$SHyX9hm_pSQ8!+plPSP zu!1#xeSJUr=p#Gbk0&H+Th=58#frb4P;BhYgVdM>D5sS-B3)>dH{`%{=q%J-d3J3*ED3F~sF`ge)CT&zLcTUO{>Z z+|Us7>wwE{V@rVK%|ef#$RwJ5HQn^v&qx8HvI{PWLSoy+2jLII(` zzY6HHrT|Tc6$^7%6BC-HWHW8rG+nPCaMK_+^T?V4G!66=Z}XynQP#wDE;oy2+qP{D zniSWyZd%Hk-+%x8v(G-8ms}R#6$%Ii{#8K7nz+t|Mblx$B3IVLyz%0TFIsdzlP6C$ z=oJKR>I;|`)@0pZOS41&r^T^Qz(9fQtm)0n@Ar6i!W=&z<&}k?RY^8t!YvRO^ z+hTt$TC_0Hq_`q(n$Ma7G;LMRMLI%(e-(f=34a!z0tXie6Re4W`RS*hw&;GwjT>j8 zR}i=KBlYm?$~xM?P9G8ss2*&*&nDB$u6 z(2u_K(n~J?7_RmJIk6^wG)pa-W5$fhwO0_hNdd8fHJKS;DfhctNfh_%90hc&NtKt| zP(Lv-(aa{rwQw_6*2L^Wm6uy~IH!P$Ulj@n1>8UZqpWGgWS~)_Mw!_w2;8)kHOrPQ zD|#I_sB7|k1c^1vlqqBB*wWsQ)3|YCi<%S{!cEhx322fXqRQL5fQj^l0zv`DDUh8t zSuXgMS6;E`4u%gOZc(q`;kaoLYXX|jJ@=eN*&!|#3Yf0|tSLjXY0#j76-|n(;HGKT z6k|*CORu<6C?FJY4FwEXlb}FLBN>Ja8Dd4R;K{gYK5LdMS59u$Cf6v|;x5frfQ{Ln zdFC0jFL%U+_3G8Lx=C>f+%&lKKP zkGElyB4^w*U`?ugv0}xn7fyUrC?FIN3dmdml}c5rR4Gd%gnIVuX~SN@({R%SYtrj{ z^UXKHqABxQBW};;3dF|7+Pq1TEpFni7Qop&6fvG@_8fKWgv z;Q9*Crd!loxc(Dc{{%U)X3?TWEj2J(e-q-HLIK$;U{RC8TJ*Aipnq>r$C_`y{kE&z zv$#*8fKWgv;CuxHYnq=2*;$jQSwPeL6vUN60Z&lCjaidf=Y*7kGfQKmHuB^%0M(@7+u7_lzcub*yP(UbPGX(@|nlGgR zYZfnF-2Cr~D}@4{qky}zrr3YdbIP!IQlWrQz?KRK)-+#D9c$u8D_F3g`Ck-Q3I#k$ z0e59hkIF>xutEW$fKb3@3JBITUrLEJ`CGVfVe`Kwt`rJ*ngW6~JuSz@^9luo0zv^( z3JBITrOuO|L7BXY6e;rLY4J`dAQX5)0l}J2$RXYc1%v`Z0S{L|u%`J1fbBp9XqqpV zxYA=45UlC3SuY-3C?FIN3K&s9u%;1FayTVoG;fD0%fSknbEN!+4P zKqw#-aF_yuH613k_#vTyhbSOe(?c>^Jf=`UC?FItO98=}X2~Qj5()?f%vM0KrrA=7 z3xxtg0il2gC?HtV12S1WqENu03JBJ8sNCYmgaSeVp@7U45UeTl3=+2|6mU5O1Z%on zmWdk{3J3*+0(MkDu%;cw6-f&PJW>I{njV?$;-Q5CLII&b4hjg?%t07&S|}hCuu=iR znpO%Yz9tk93J3+>VP(Uak6c7ps1%v`Z0il3UKqw#-5DEwdgaSeVp@2|8C?FIN3J3*+0zv_y zfKWgvAQTV^2nB=!LII(GP(Uak6c7ps1%v`Z0il3UKqw#-5DEwdgaSeVp@2|8C?FIN z3J3*+0zv_yfKWgvAQTV^2nB=!LII(GP(Uak6c7ps1%v`Z0il3UKqw#-5DEwdgaSeV zp@2|8C?FIN3J3*+0zv_yfKWgvAQTV^2nB=!GEm@u|NGzf-+w=2#*C>`r&h0CowqW$ zDRB!z0il3UKqw#-5DEwdgaU4%!1K>PuTUsfuU>uO!iC+tcZY|EyTxOPhY$(~1%v`Z z0il3UKqw#-u$uy}zWVA1PrlxJ?>$aF{q)oM^XGs6{rBH`>#hI$zyIT)aN)w7|N85% z`}Xa7^X5$qoSfI)*K^N3SD-)v>9QAIc!Be@&|i zqx-rz5DEwdgaSeVk5b^JmtK13op%ZqD)i!uFM8C2i-#2oc$NasKKpF5X3h3L`P#8# zN2^w?LPA2;ty{Nm-#-418#iv}&Yi=D4@Vtvvw8F8_wV1|yLWHt(xo{AJ_3{-J9gZ( zX%i`N<%9_nyu7?hl`6G(@#3_!wC}$APA4mkOVz4XH*eldYP2%C^Ws1#AQTV^2n9Sx z0iv*CV`JB^U%zeJw(i}#fArBup7ZSDNreKQr2xiAjFg#~nb)shKYjZ2nMbd~hYxq| z+!@5YapMNCIC0{{^XJe1_19nh`t?H{Op?o&FXtZru3VwQCC&Eco!l4|V6oflxpwAQTV^c#Z;CI+y@3 zWXO7V}hDqg(!C!c)s%P+s+C?7p~ zbnV);;m$w*{IgT1PK-p=YBlkifacY!SC=eVQm|k_kdd#SJ9n;4n>M@!Mj4^L@x~h_ zSo4oR{t&F`Sw&4etx!NH;O+_#F7?${UsbDCEl%$h85vokM2UR)@_BoEM@L7)mpH^Y z`1adxUw{2|!mj%F?~k#AzDD2EML**2?~ma#I5?PNlq*)OSXfwC#flaAmQKhozW5>{ zA|fg(iatwBSJlZP4uk^oQb5O=)22<+zcWHDX&`W*!;~jao&+>`;U$0b%{P1Z?#)TA z$MJ#LQlrs)_St9Kw{O3D_by{o9O%6GX#Du`%vI+Cm{qA%>Jpdinu0ZTf`|j5fKWgv z;2sJPh2O4SJA7RFFU*!d{`h0BUcHVTJBCT(;K74*3I`4xNKQ^>RseiCV#J6$ckXQ2 zvPIWZK&AM{aJ7*R&bC#nRs{tG;XR|H0ZGA1Nv;u>%9ShQeQ*(vu9%^<%{LYmBX=y^KHY14R9(Q(9b{rT&7GJchXZl zi%`H#6v&A+aZ+^Y(gh1;N=gboblM1NJ`;vwVq)OSHEY%|83^3Ga^(uuyL|cbM0OM0 zxpe8$CQX{mn>UYZXlE)_szjSYpl51oD&tf*m1*gCOo-PMc1@!Ni23{V>(^(3|Gjm=dsc>(+bs?$J%?eDQe9=yzaErmITEODq@IJBaB7YKs;v3UvPS&p$P5 z*3@5OPyZgou~0xL;4}pcSQF5E<&{@pMB-+6QKgCU1|@NV!;X*;?FDTClZd!unhyq) zyk#UxI?NDaOi5KIu!c}><{09I=M3MaIUrJ;VJPENC<=ZhI!DY6%AV^GJdL%luP?aC zSf6F5vR@<42nB=!LIHUykP~atK}eH$h|405AD6epn#}uQp%R?n9CYi}4QmD7_Q1eE zTx8fRSQn2Dg`g~)V+3pU?Aa66#3m{&Y{CR1@RaCJfSi66Rry%|s~?6#w?yZ{vhwK~o&=XwG{4*amdJ!T~>ofte=WbIZ4Q za-o1wz<~-FWlh>NX7d1!OfzMpdglXY+14(HKF@Iu4rM2Wdfm@{Gz{i#QPZ5a4BwcWP=FFMQ z-@+$UN0~E3n*s)bN48C0hN)o# zCJ2G$NEw>zUbML^-hu2miD-G5T#2EV>6R=40#-6KC-{|Mc^X+P#E?B93Pje^C@)&H zh%g44Y2s)Jkf)`lk!5>&Sd+M@_U+q~&VvUJXngrC?8ih$@m(3pB5p}2AQW&v1+udy z76%+$v}~9&Y4d6208QwJ74(>R3JJlH_3G7=S~=$ZFqX!X#!MibU<}1^?=U3*w-|%- zI(6#sMaJX|#TlX#=n1*f$^lYL7+~QPY#Ab0Q@>Er!zLysGQWbphIO)uG|>N4918^; zssQju6=mw5&Z22Q*5tv!O>CbuNd%00Lc1WbCU#s7&{=vBX~ZC&lUN@auj40TP!528 z^UXJ82a!n(*6|1Nq5;L&Oj`_nGPR$`Bo;0uj+4K%;QFjdo6h#31jyru8aZ+#GqLzB z093L%JG4lN9}@})1w2v#1J%i|ML#y#@z( z#;`OKw}9-ruZshrfHewGO!Y0Ac4bX#?$4FKqhn2JIb!KiXg6tgaR5nEuZYeBE+y8a zCBE)L7Geuz(1`Pgo8B zRx!26l?#rzccFmmDFBYJ=8-fvh*plVF{_kFzOu4q%VL|f?IX2p*^)3#2I@4VnC|eo zvk;PPe?nHN;lBb6=^%Jm-3#X!-2|wOp@BXEH<}chg&N={_s^^ex)`n}YosioYhZaP zOvrqf7weu9CX1zGz<>c5lId0$1Mo$_0JG?xJ$s~N345zhp#r=sD^0}BxQqfA(z1i5 zoqXsnBZ}>ARL7dYrGBc^Scc$?L79Dp6CB?+tjW+H-#Sevtp<_r_?vJrkvp^tELq0| z_>3S_)-I;)z|%ydz!#a?&(L2=9NZ)>hie$_YqB&7J{)GCOE1ivw6}JBK<6e7YXXi~B7i^Ex0jw81Z1OWd|V*@#*G_U z8H%8S+qZAy2Sao0$5Qu!wSw#MeGxc^R}IjFSqW4i6oEKbA{;P+5>rbegw;vrVJ2*` zLOy3Oi_+t;91T+o#J2U$Edwt3>x?@I*(PX47o#azG=*K$hS_P9HBp{`B#4lROdK36 zZj4zyn3LuQpX)o|S;TvMm2~grRAy|{qDi=3s&;Z{$l^>>LKYGGCCFcl!dFi%k zN^qc%I873*2F;TVAIGJWLUsa}Xj^%Cd3m|?vxu7)3dm3asu&Fp#xp7!HZc93#U89l zTZc;r^PBYI*uCSjB-X@j1p?Ap{P*0ifcOMr(Q(ih5D3fSP{1g``j{%QS%8>y8BD9f za=~TvEbQTgF#?Ae%^(5&T!!17EQ2)(*20Y@u_pZs9S~8r(uy=B&H@7Py$lPUxFuOD zKzX95>GP%jF|jWg$z=Tiu5fckS(By#XE#PlJm8EP887meZ9l2fctEgUvT!j`t(ZI+ zp0cblLrH?)srEDkIM$(3reWf3r&%HBlk@mTHgDd{8cB>C7(dYFV)dlC`Ty*_2Y4LE zm8iS6Y%5rjZ6!;VtY9T7QCSql1Ok|xF@pgx$Z3#s29b!woHIpHlt_t+ltjtOmMvM5 zZP{yk*Y)m^I?alAo``+8{?Y_O=zJ33&$mVb`Gw7M=2>_};eqc{`bx+T!Q|J73 zPE|Fjc2=;P3vx=uGZIV2`C)T0%A|^rRVuz!eutF6LXrTqM~Zg&^5wyxiDyX$Yc3?Y z27ar;npk^$MDWii)V5L3|7`=?pUFEaX8WXNP15^8|ShTVY|L=$$iYh9#mf77Tl$3(Ej-&Q?`b zfuszprze0Zp#TdSLzWDzVa+4LpJe1=Q^KFv5r*u@dFa8l#87v-^<;twB(u&`@F}@3 zB_Jh`padd;rew_o1uHI(!kSF(Av}qk8MnaTOYjlaB>Dhc;uMHVxZQ4ml7&kNa}c>C zJi!EimM&p=DdzRFG7PVq2QC3n{xaeV@57t{Vi}MtXBijEDaK`yk<;Y2$eSz(2{2EE zHFZPaGUTIVO-)oGgf;1X3dzHobWF(sK|gHl@O1>jNpEj2E1@u)D0u7Z>mzx@G%5Px41UG7l0V|LCHx9x z1)5_+8TLvSU;Hk>y}Z1f#iuBb`p98snnKB{7}>LoA0bWvo`n z2TQ#Kh=eg`nK9@gu0(v4am~bQBob?ij18;e6W4?k8No?BlD;%i3>M2H7>T>#l`Bjk z;uTEU$yX>h0V`WuTd^S63%aeK6}b<= zsY;pox5MO03?eI-25-P3W*4#ZZdF*50>C99WPUza$ugz?^G0}Msnp6upAS@;kT#`QgaIwLK zge_riLYPRNRifBuyaBe7OcP9r3Faz>Ju!5F3&nEBpeI5@bZq!eEHXo571ksO%13(n zB%bK4kyqjriKlo~LtZB(Fi#{PKr>QAQ@9w(nu%97c8PiD%nee;Yhtg6V&JTaVi4D$ zfG~gzvceZoFC0-E0UtRlK5|wJ#1cm^j5rHU@lo7_vxG(APL|DQ_No}}Dd(XlUWp6h z4){(r5lvwlBx`D@6O3^QVftjSriKqZhi)ZUldd_lse&JSWMl-Oq?b>imu|XXO^hj% zE1^u1N7zsX%cJLX`birRrxdSdCiBinCV~MZm4HjEEinV`43aXGmVpXPeV{3kdtw+q z$@<_gmHScx3rzyFshe)PDLQCk{UmE9t{5S#$ww9{5*R&V7bX5HRLSeuYofK@pzcZ393V}alYYa@h&nWcQ@p9F|#;#p>mXbS%#SyL+o z00G6Ig(|^sUIZ)#5>}NViLb(>=D-Uy6dik7ny^}FnL}C41kKl6d?A=9RzCvsl-7U zLC+*j78YS`R0c@V*Jo}fE2J{KNvj_+Kkmc4D+JE;Db{vHT=U~c$zL&30)QqBK}|$c zvgS~9@>nyGN0rwu6bax^>34=GWmxo?c~G)uB3gpv8SD~8@{%`<8F ztbV|t1?K*XajuFqlww>ZN{S==Rmvzk;?AO&a!~GMhy>K9&K1uhb(9j1wH@J!Dl zSyL17%A3R^0nD_@ku-`q2;Pyb8IRCUU4!ooYYu|ND_Jvc@rGO&%$lT@Si4>rVp8OU zUP1~d@(~y%oXJp5W)v~sh_Rl$2$bQQiQF^uuNAYPSb3C%`jm}n`z+wa%d40~#mXt- z70hB8KgS&SRVc|$dxoYtLw8S9|B`V%D!aH1jQ>ak4FMHMH0BP;*O%GVRs~J#vytU(NrUO$(nJ9H|!;h z5+O(iodkm>Dy=zdif?Bn;e{7Sxe|MZJ~__YnAm_(2Ewjbv4U~5#5H+89=C-x36WBaYgrgo zuqL5RR$5WMRPLlv0s=I}0F_zY$IJqfHRG;3HIkRC8Mk=lh4DxLr6kOrNa|d%5t21y zssM0@xy))JnnJ>oHDeNQq{2kekUSVP(*-6et{Le`=IilDtcm1_?DE>rEnBv*20myg zSd$ntxpETaWLm*cW>5i#49g}Qv!e{ zFD*^P90c17$(rh`fdmJ*GpjiW7O!MY_2P|E5;MShP*+r`HEv-vSzHcBP7q7?P`3wWX)K` zE6c|z0irv+!bFWysx%XlHC5I`9H2=RNY+#--YCWB@iX!-lFm!kj3PSqr^2!(-CE+8 z%LNbwXCz5PAE?HBJcq5fT=aD?#D~jYL zYete@Ovh)kCh1g>KVgDYQ&SVmmoglacLgy?h_EHgLMiWvC-%vDrDVogE(&B}h7cuL z(3JW6&??648wtH!_Hm+`44Vm}*M%!U||cv195_N!C9~qFyOObTraO$(qsYL*UM%k3JfoYlf9xtOtWxll9d>d`0riB@do06ziQMm1h_z5mUx@O4f`dq7)pb zm9dBz9qC98BG%)QHC5Icl4m>#;)+Sak~LL|R~Cy$0xF$uOe(7smaG}2N{~Epnz%s| zAxqYbBHk#cgyf?~G=&aJ){G)X^{1Go!%{?l{No?VoDg&j*7+o{CWBI_JF@CbOD9vW z{!wK~tt3Dz12h?wrMQE{cO+}7sU13RfF|9!SOqRwQ;m3KnK>L{p8^ja6$!dC8iQ zY6U~gl$2nn8;67?Yeo`pl;cPqLmXv{W^hGF){G)a^`}sE#!&KF&g-wgo}_tjEICQ6 z$+OXsGn>s0hl4D<`bU)|HIaa@Zw!+NZcT~rOV*sVb^uKVF(arrq%B!yyk!qMszVoIxAT-x_IT;cqD*1AfidX zGkKtiLQB@1SvSHECk~p3SF+|z@kTi!di*LQn!+4Q){G)g^`{u4N$PwiXd-0En(DijEno)EB`z8jcDB_BBN3v!V@yb*2NC2h89MEo)3YsXoWX+K3!4Q+c z)dVyVuVl>-@kaTG&NIiE*UYGlXbLkeSu=`ARi0w1__fzwo0XLn*7Hf$R4K+eQH)l` zcnJ+B1BK{}WX(uwfaEW~{BpW;Argz}qh!rU;+4nakpQ?u?wq(s<@kZH4@~oHxXwFp8=tm@LMvkTQw47(#)Gm-%3&~arvZrCc;@StSP5UoJ+q|ON*X(D9FnjzwqALEe# zO38S&u*35;QPZ$Dk*rBo33EVPEv$$VIVf2(dP_j^ys^IdPo2W|#zaepX|pNdd{4idG2w zrmatxtf`1s?!+Sjl=2(j_=dJhsnT#PaMt7%^GH5Pph*=-){N91gya*6XbKINtQkqd zk&Xi&EZDBz+DO)nB+1M_PF-Jf%{3_lO`4=+O`#P?o{Wu#5lvz4Bx`1Vqs!bUL@B|Y zIA-7qtQl6qkq%;Q7^A5kG?BJs z%}Bz`@Z-X>q!KjI7Rj1u1@`T_>#j?NXOXO#;f*eHA5SP{Or}4?*CzOJ!Lp{nok(j6 zCMZ?qa><%A+n$D?iJT>C&XjPZBdnu|3yjs;NY;!b&rCipIu)RaVvAC_D=bwMmO*d(%^O7|~ zBpm4@I)BS8x5N?UlB^j?q#1i$fM!lkj(SI+Rmla;n%FlyOOim7I+CoJv5hWsA54H| ztT6_yEP`^M1<0D1gPU)@IR&7JkR@vdw?HI+>#etH8*7oQ87$#Q`vNrM(|O67k))cL z$3wgHTZ~RO+~_yb`sQi$(oS_n}NsiEG%Ue3YvPoL9YvLHObpwu&hZd zqoqW7|CS~)m#mqAjV^N^NtlDQM>K`%FDTZ;9HcCwDa0#TQ?$biF1R2Wpox$rYeu(p z7hZVb?YG~a2;xiDj4t7fIQzNJ{mJLQNa~#NxFMjaHyT&D3Rk+z3`VmixD)**3zId4 zXGv;AQ;1!%W=1r(Iq?uErB*X=qqljLXBQ4@rXDn@0?C@RN^obKL zB)i8uS1-EgqC4-rGeOBq){G?KOga9!&;QAyN56Qx?%M0F4X$gw!I*1tX4~^`U328t z;qMq+MYFc1#0xAy))by4*%3`qHIg+mrO{>hbD)%|OyH_991Das(*l~vU$Q0<&E$b5 z5|*qP*60D6U;gr!@4ox)#3zVI)(k7*3_AG5^FP1!vuj3vl0&Zi>}Je%qJV5i}i!Z+GuDgPxT(e3tQUS@DGbNlsN4{|W`IlaH{ictX^gOS( z=8-W!U5`f7N?+Y&kN-_}@AJ;mroy@*nM2pS*6=K}jufQMm9{BaQ}arkZ#N{m!$`qQ z15Gq|-m@mSlg3!`LT1erfF=qoS#zeX1T^ot=bpqPn!>|L)|@HfOgZv}FMV!l-pwoW zs+tDu1(gq1c4ZYd(j-L1<=Q>V29CR$Hx`$c7Z(?cgUSB8aN?TSH&)e1C1_H2k~K4> z>1F0~Flvg45hiu_NN{2V!aNfwh)J%e3olsK)I{=XBraJq*ys__yyTKg zGz3kmK(c1AgfnXYf-A1MaNqA%44rIj?Q}W}UwPs$mkfN{V0DUyV>X*ADk>@~E2^uj zolXuWJ%P^mE)dqlzTI)h9hi4DHcG=Xk~K4`0cOy{h-6mc!}B#%){tAxSJp)Gn&6Jt zt}ArtP4V*P0%T1j&qzVWXrfvn%9f0ek~I~3hvYB4^wPzP7i$&K6!j%pQ;~2+?GVCP zbngR;*S>1+IndDD?sOP$+xp3}hJ6N$OH`c6WGX8wD=#mvtEXIlDT~ck}RztMY2&G-RqToA<0q-XJXzO;H1qHG>*~+&v|M z)H_|(*5I6i^N%%={8zs6mHY0yFY)LOq`0nj`AAc~)f{JFb3w7D(0R?{YUk+dj1Q7E zu{S066mBq#+gDgu=V;%pm8_X*K8^9Xv@&c9t)Z~6kg#QAVps>DRrtC zbE)~qnxql$z4zW=n;WxG&Rl>o&$ngw_jFn;mNUm<`+woE=I1~E`D7$;XBD?(O{`7h zlG^pYi2_@GGQ6N<%{04{bH5(Yq$90VwA<}&YHDg|Xs}wX>?p@+I5dsCygUv!OJCUp4%E0_9qeD(RO%#f(jbt^7+J?$%w0g zo0XN77<3GG6fs6RwLn-C(7f!j%QT54N0MZu;|rWM(fO}^?Q2;Nt!%7p@D=(DXt&$@`}-;2a=DtDAK>i z3m;x=X=#bii_cfqJnw=pTz1FhH)P#(^?l#C;42pbnh!nnP|6lG4z8Vr%$fo;A9&ya zbe;$n!7cs(2RXHwM^Gs`4*#8UAO!0U%MGNEGnICaY0h$j!_@L&XiB!2Ku>m3k zaY%SxvgUagoOfN$H+4@KD}Pc`aH9A@&%;X|Tap&gq(&AjYoha)Uw*lw^T?lUDLfHh zrznbUOV-Q;w+3jEH!$dO@9jTu_0GRE6gFnt-75odeV@+yk(C2M9vV@&fih%kH9)Oo7c;)(f#m}Jc~yOuxs69Jk5#L?+;M-Sb; z=50fM?Y(X5ZybG3?<^v62~7?T4zAW2mXvkmx=M3R=0!utjpMHyT}8vg!{BB|N5`3d zxNf=Icjd0%WP2NDt@=0$%qP~owzKoj!-pS5?5DQgo*t*mMHS2<7PDE`-M!-HKiB8w z%~CS<(~>pQ-Wb#T0GNKl?2#jy`kWkhc6NIz*Or^vSxZ4W17Dm(?45W zEqBS1p+_EmNtfGcG+MP*zl82Lr&&{ldO#{f*oHG^=5!!DOXfupAPrAocY0Y}l~D>pdr9ZnfIVD=MtEc*X_IY=gl^<_Bwv zT-n2=rT$Huib_lMxw$p<^&v70{?)+Z$I}VvKCgOV+&P+DliAu5!Oy zQ1gD>n&*eQHg^}a7P!aqi+7inOqCTj7g=4Y>rgEy*8JiZzj*Dn*DhVURKQ2Din6aa z=xc3NPZaE~x7JA3OuuhP>V-#=GM00s<+v!48(*r4;?yGT3XuL z-jP$%u5p%5)f@Z&sSP2tlSpn|Af zPG`s3wYfb#Wyg=__VyYBulk&2BLYf|u+Egj;h3dp?58DbCf2Iv?A0jc6<1uL49}0E zIaaIB>+Q8#i;^_^FUnU(KQ%{L^StxUyYk8_>G7W#gcSW(CVo~wQllHIx0z#HQfB_K zCNA)TtAw%_lK_XrjQ2W=e0l`T~8c6 zUR+W_6f=1J6TR7D?3{ES`m|=-Nxi#L0I0ZJXK_5*vT4z}A7r~LLT;Gg$GOg$NdD?8 zu6StWN}bV2#yn@`%JTQ$&-&f(^#A&=Ilug+y{ju&j5B3LP?N#^ETPY!1<9Iox(#N| z!++_#^UfE;^MgB@GnG#qp|8X1!jhqUk-kyo@j1tuH(qt2Yw0(ZJ#h2GOU{`Xq4M{} zSB#iIf!p3(R~73d-+9KG7-HgTblF?mS|`>|9D3?dU45N>z~OnbpyEtFlg^RNHv2h&wAHSHYxe!{OV9l8MH_x?Xxe5l26dDzw$;s( zi}(G-ogIhuhM?micaQz!oMz1cXkK&8(%RbW!-tD{dwG|9X<=c`KmF6P|NKwGAOD#1 z``^1pMxwALwup61VND{+uqLZA$1cBFu8?uf6gS2+f8BZKU4O~NCD(t``{<*q&zgg% zNg`bF685`07_jR*f}VO!2-qa(1DFD}nhSUA2%|6WqZCVnCT~ zz?A2)v9UdS_B1v(+BZ4PFWYQyIrBcuFaD^k;iabbV;zNUMJB73J}g|F1dc5P)>ISG zj3l4Lk4x4}rN0T_&ec~hU%ouVvt&EGODZP}wsY`BZ#3OHdF{{6}$|5m>KRlS$j zoh!L=RVKmYmC|M(C08*dnP>@XJ=MrTbxlZX;-qPV!2@v@1x zzK}ae)=Xt%O#gRXbdmn%Z@rnd>X^yo3sHqgAJIHk(~bL8hmmfZ`0=^Ln&6IO^}Xv_ zYz?JWt0fgEqevLjKQJ(`U|AE~0W>kAtggbycf`U%D%-YgYwK#u+vb5aEw9-vC+(h> zy)}nwn+`Vnx{J+@RHV`8J!>NQ>#n=*vBw?@fe%G9lf5HZGi5GEbmz{AXr8TIhU2o@ zj)Ibb6FRf?k{AB!bFci@7x({1&YIT@javfiYo6J1tj=2}-n?etzgo6@W^P<>2&~_J zdL!!5(AsWj7|!ZBVl+wC{1ZU)o8SB%{)$FgW-__u4D3Ec@M6_L8n|9Dd^CfF0UXN<-0BC|c zD^{#f->J}>%-8Jx!55zWuj}{zt7YU>gMacAkDhi!KxAS2+VVZ$FX`J+R@bJ>J&U0< z;kLF`yWLjb)WU*pr!UarzquIK1T?R|{`#z}ECi67lhZggW%K!XhX-VtpP$d`_GG}Ura)d4p zX-B#{%8?A&nJaNkB#$AcB{Rhj&`G`YV9kXY*F69H^O5|@l`B~%oS-J*d4$h^5V&({ zYKqWi(^zxfVUO#5Pr>(n^{+NI9&9QftFRT=<#^2mO&*fR1*S2gDO#Ci%>?UF&E+Dl zMk@zm{?A)=fpup>|ueTNy>&$^g zj&MUD6r-+*eUPKj)|}2c))b(*YSk(sI?Gc z-j}SYrZvs^G9tRef+KWUSm%r(nCdkKLSob~`ltSq)RdePtSNAZ1P5ZrI4Xu9H6jw< z!i0B7E7lkQHO?#A=dS8saKQyP-E? zlKORe%h}PEXDUn2h`{>Brt-B1C-=S7-Z#PmoM%oMFp?D&6|~d!>(}SG-PZE@2RFX< zn3t(VdYUk?4_Y*BN6ckWu1b7oZm}jl{e~NEP(YKPkKA~(Jd(k5L*xwEWPet%AX022 zaDTca7hA0ryLK7QAndGnP+35-X0jV&`tMHymr+w2&r%UP_r%OwQ zHEI7Uo29nJupUGL&RP?7cc0U&DR2kSBqUG&-tYIL!z^fwqXN6vtXV^n2)hjZ6XUG! ztsmVxy634qRdrRGPHde;(0BqIMtrK3u~!Mi&jip!`jR!1swX0%88LO9n|1AmH*aGG zx~D3*CFsnSJA3xj4sEm9teZD&+P-ZY2?wti+ekPg5YaH%9Q6$iSWDiP?ky}_?rZ$Y z6Mvsuw~3e=*&#;4099BgG37D%&I#_%A=boh(47;hbLCev)D1?*s<5dy_&`6tNx2EH z4$7UJH2m_HuGOp09@Jt_vSv~nWBP9nXwvUgl3B1O(Q=h<5fBPSr$@4892Kd=)i8^U zd8IE-4^oRZ8}mJQ0AMTDfe>?-H81+=MT?d$()$br6$LG=Eu9^m=sZ=xlF|HSMGrs| zfpm3su`@kA&Dqh>(S7^&aS#UVn}LsbMWXR_k~QNnTq-XS zV>H93&P6rqR^QvRS2uRT;5ug>YPKWq%TN82Y5dzpgKpirb-WqK?r?aUx*n|>HkfUz zo!-0po+xb_g@G7Rjor+)d+%<4qNuKq#mT`_auf^-!a8A2Au4vXQkuEKnivffI|MWl z0-z}}miTu}nHtF;L-LmAgduD;^N}N2zx-uWMFj?v8=5T^J#e4!LNt;!Rko}-Su6xJ zQAczO=m9&_w@=BManyZqbO)JZ1!(_j8o&B_iK!%jMXwjQQGXRrI~Q~47k%x^i;M4N zqEFL1twT?btlP3~WO$^Zp@G;59W4eDAPQU*HPY17MBrq6e4GdhUTyE*y+k^d0rKhv z6?e&b#+rcUZMWSPoYae3GFn|ISu+mXrREZFhXiiKh~}C4v%3rH+j-&KIm>S3T5PxW zKEJYR!sT)@j)gYpbi3~#IrOdR&kXrB%L}S5Iq>(Ep2Jm@6^t^+TQN#gXLhS6i!L_ti&vKdud;v zVQefgQlX>-X02#wFiuZr4-Xr3XU935y+HMzWKA_KYfhE{cSzudEJ8u{f&QY(A;Ut8 zWDY&n>KCbVCAvdsfadMD-##ZMeRp*W&ONWi1vupI;|L1yJ=v{t?RyLE;ya=#iA}W)&w-k8-#6T zxjNm@@UY!x^PEcEsA)~)aPSNCz?H((RDeEjDQ;-6y!P6vBS%=!L@61^ zj$}d#MOg=Qug&lO0xchd8Mt zW()DLudk1unAWn7c)$6k^ZbH~M7|L~YSutY8! zam`Z`{xns*kDpFXGK!~|J zBhqumdRg=GydxtyEiE#xsZ~|ZpZi1tP1ow|{2r)*2; zpA2M8a0mCML^OF~M8^b_WW4e#R2)%16IiV%FPjlyh;|KCKZkPY0nohlRuZ_TcC+mT zXWqwN|7Q)oZ}m6tZ*^Cneqo7FW`ZS*XCNp-hK^;Aa94sgaclxZW0F!d3C?zI9(tWnA%h{I+^+KQ*zHA&7O_?r zGaPvP18;Tz+Sk5@dK0t9ybwfy_Lz6Ctw>egjR%Mbnh376RR1@B(|6)T&L^Mb|NQ4> zR$`xxHIXo454Lr%0j`R~&Z)XMEfoeGF={gqoJx3Htc+`_tm`>bjD~T`Ew=>Yj?$Lc ziGwvU2Lyo>{o~C7_us!`)v7%P1F^ao#{UIBh9>)&#+pbT+#$83{5ph2Ag!#lINxcu zThk^htawAIeW2D6(_%h6{9Iv8A^E^aLA}9PYO)=0xIWCUe6PCkXmi;}nayhxUJRy0 znF(Hk{`BcLZ{ECp`*sX2^+jBhQw(?tNh&E-nT(xzz?yjCJMX-cmY59rBz{lHni~48 zi!Z(y`!;K*LZ{0u=$=|tGp4sX&&D+&m7rCPM9mV>B)9ong) z24s8vO)aZei{&KstDFVb55072-IE4=Lb+Y#{){(+=dQWt8bFhkTNseaAT%~+e6w*1 zbrSoW;tdg3U3JxEmtA)0rI!jG1;mp?qS!YeI7%zixmdE0 z79vireonN)n#$eKPnT=LV(H}_s>;E%>|_pWhDCguyU$l#QCT%mYkgtVJl*PWIkaiC zu}T}jtn=n{muAQGdQ8kv=EpUOwP1)>ty~3ATHRJ>m8-bEWZTYdPrvx|#O8^Tijv^o z5S|ZvD)^EpCX;~}4F+OjVOjHt2sECISPT7oetWD!j-&jXX3bKea_YYO?z`un zd-z7iI&(L&k&tW1H*%b(5_uRxypcBIM=aUK2v=f|k;()gXW$S9F!%_Dky=wOFDxz1 zA0IDz=N&t*ePQ~!x~e}@FX8`}teKqFncBP3VNwH{r~s^qo}pw)0}ihjXO7aa(cs|8 zxu_#66cf6ytp}M%arg)&Kpu}Uc#Kd|r%Olza ze6zz68$V?}Yohak)Kb=Jqub=(kYDvyZPUkX8{gaf)QeB;+`E(IRKQ0%ONv}j-Rj!v z=C|xJvUMnTj&Emib-*7Oz?S5?>ITmUJ$f5GjPYwpWB=WEnHWTkuHLZ0aO#!oA?+)skCHVLZ;WZaLnPb@s|SKL z(Osnhhb^WYgf(gH=(jk6ML;R(ZYl-RxRZ9)MDiqA=-19nEm=+avFfZ>r=45ZR5jN7 z^E~;4InyYsIKCPU$=J9ik|&~xnGQtp%?4|~t>m-v=6`GN{m(Uf{$}5y_n+8!c+>F2 zFa|oC{*S5>nn}bdLJh2hAY#FQh1Otu|7CGAdv9!Ldq#Y`YvEeMF20uY(FLlTCa z;(>wEmKF$Xvwc_7Nc_S#XF$0pbSTia@peNa^-m07i{Ux^ADiB#_w4cd|Dv($ z|Lz<8!T9LGv7VtGVg_X8P;cH^iFqZ)DZ2RB>eF#b})kiZ83u!p$yk{z_shwuYUY-L(rVpV4?SX1ac`M$G~5BWVw_((_w zPZ@WZS+Zu_E(!pO6*Q^;!h!-YMd=&BnzTMZhh`P59XWRAU5$Hk3mQASx^jF~U*7k3 zD_ajJOu5?bxn%$Ud}Q$LTQ_`nQOApdL0MtKU~|^gG6<&Y;ermGcEdE42ZJ@~#KMKl z32xU?Q?1*7P)Cl{W!tN0<1wAo$_;gU7ileP@mcP80($?2k^u`+< z)6;rZ9cIX8t};?qky#8ok~QOQjA_0Q(@z^vAJG&_5eI9cnrJzf(*V#*CB_+pmL;cX zx>ys*V{wS?L~**vip6xgyL&)$t>*D1M~V^e%1_9&9J;_A#?%MR4=@<=jpj{O``a$} z$6ntr%bI`Kw*IXRyI$GdKi=Qb(ZT9{m|C{No+A5GE{f%S-#hfq$iUNUTE<%|swxP& zP)8*GFvq|jCYXgbG4AX~6pcnF<`8QN$)^}J5ie|#28)vsR*ZTGbk$%@Tn582N$H;3X!JUhgE5!QU46M$=lxY{b3Lcu1{A1tljZ)Z z(JWu*!_~u&_yz-7V75M1-MeJ?xt6w$%F2pU^r?MUkmc$a5G5E9Y82xv;X(m|{3XXN z24WI`B!x^hF}A$CLwb+zJ>9c2tkk^ zCPHkA^z=Gi$@H|Xs)`sgjUuqETIG7-h1^$Ov9-2Rj061TDttX+r|n?ks0gSOGuYPF z24Z6IfKmPu0cGPj7)nDUu0&6p)uyPm+V*zacfOPR&;ML>@SxS>$(f$c85vQ=IVjJg zen+xq94#^ZmtgwQ&R9VcwTP89!6~Bm@Fg0IvZ9w6l0fITq=hwAI$gwt+z6`C%T(VQ zGpZy%A){CmXdvnUXoe!b#b6n-@~$A(Z!h~%)xi5}CSRPIcyiL;>IWQAT7C$d$!l`$ zb?1F2-}hnJ+Ha5d?CY)V;Z?ttc_n$3wUr~IBe13*8!Arp?2}y`_yaO)>9;1cJpq!( zK{8n(RPxCdkl}Ry7H1jXS4q~Kt!~4dk1wK0Ro&k>eCPh3uP*I;uzu~Tie7`!4B`MT z%(tTf&aA-|C5?CO{v@YZEUr1T0MpD(ASoB>EcORlx85}UrzK@ow9T2f(&PxN$=n^d z5R`-qVL=QIVFd*QiFAr2I8jepE*n8mQsiQzncy7|D1X`b$R@JqqOb^;axI(4q;o^D ziIh4aQf3H2hsaHAL>#eT_+K^=<1DJGDu3&(%AGsQs;Xc_c#&;n+qR0qLDKCIB-BXS z9Vv-err5y}va#G7K#5Bd5lyn^*gAopoaJtGv+1AX<#|DetCGu z4%;t&vEu#rXI7GW$xGG@)7Ucn5PD1lpx;U5sjq2KG7i>6^H6u;hEeOxiPEK>HIY09 zC=Bk*s0QRrZ+@V!yT<3pcg(%Pn*1Q#L*}q1RvYUN?wk|RJR^2vk;%EkRq#Du^(X$d z-<}xRJJLPTU0P9!*;S^&vEo$8n$pUbs@HrlJo40N%V=x$)|$Gv8yY@p9zHg@e%l6K zti(?y4vN{uSf`**H78gT$pf0C7qI|m6F5Ul+*>#)4o((<-bgpujKIH4MSZ#H6 zb-|~C_Kk!;1F80SHzpclVaR|Jo>cgf9f&g!jXbqMqJ$oSLN<{!Cj!cLDmQ-mkCHbR z$GIU0%Rx?ydFWz4_!9OpwipC}68IAvZkLTC{ADC18|djqn7{j-@5L8+9R)`Ol8QBh zImr>_5<#f^4P51RgOY?T19!>G;~=RUt`dXP5h)`<+3;C(42XS_%&e`iub!MNd*X@e zo}QYvHq*g_tBxK`B#kavGuFnK=JEn};!bA8%9`kh$f1LnxMB1--iECGbtz^|^`Oa5 z#RPP8cCG8Je4@`Z(&(_;(zCK=B0|p`))b)0EYV1~L&mPUvwH3E@wLy7Pac?T?{9Bx zX~bUBYo~KBVk-3JYZ_|WC)&3i+1}LERNP!r{9IZ2Z>#J7xq152^}C+iT~$|wt;ZZw zcj!FkSX`Kh45MG0`K+0C&_sab0jWoVJ<)|3_N-*hnA{a_!HL_mAb*|3wj#e?XZPOG zbF^^HHZ-3u78}l&XFI*wY}o??W|o2?hpr2( z*^Z$T*)#UpgptJ2a}{^MnqkTa5`mH!H{zP)$8qJXY03*M*x@6r$#JshbhC+!azjXz ztC+iPEhykde3y=R6YJ_H(BYJ<8B=3SbK#h9Cv5AAl{LwlG44=wj0pC`oy-dRFbN(^ zC2JyiV#z8EF%6zhWNTad+7|D#!{&(=mYtBS8E4x;@)+ePpotbCaQfmqcJA1|Yy0M{ zTLuOPu$x#+@<&tzHktzT;91%O3%hZ{#?q2fQYsM1+(j(tf* z$(rib1mYpImgq1Ea^-q`w!>$0`wU%uUq1d1kL>@A&Oc2X4W|#lBjY&3o(fZ(R*W)4 zV9nem1vQsV|Jky<`cqf(7))eQduQ=wI22Z5XJestKoe;&N{um>SboC21USibGgU}r zy~%@%%s5$cY?OLA&nzhV0e3J!kqmK&;E)pC#Maif*80j$f1%IKBJLR^He+Zfbb#->t zPdDU0Uub>P;d;l@_)OD*mk+dbwxI1OdAYy5`arG!{rdbduPK@1AJCJGWlcbnOd``1 z(hHhYF2iwm7i?*_HCqiK^Gjw|WXYOp+Apn4J?_jd^fM(Ms%|;&xqrWY%kTBgJF$>( zA{ct+WIX@Vib1O?KQuY>K6-;IKeuUWwJ|Whb7h`yaoxm9=h@d3srf#%otZrZ9pOuo z-OLk0f5>+eyaX{51jien_XgNT)43`L5P@8XLnK;SdkU2Dmjk#r!ljw{Km zNVub@X7nBODI9C+v$K7PXH&zPvnFuO zMnk^C=(eAwNqUMTCPep<2A?v4X!}Lfb$VG-Ab#|SCO;V%!pOpr$b4$s)X3{&U7z%> zJGs88yV+Y5u*(=|I!h=puhuwt@F1g%i2`#$5(Cm1%bEf-3Gie>dp8@*V~&ApGjYwB>q4^TtZod10D`AZe|O`bcc|_IabF#ts5R}+i%e4gq-4|%jtx? z02)&H_ti~YHu;kW3J1bb>C8({A7M~3Axs3qBp$})Gc=R8MG)6#fIjG%4B`Yd`BGXL zewkc3Kq*q*Fe31W2O*b;zb0}aHnC5@QZS{kdknIqQ;l~P12OSFm^*Hzup`I0kMgrA z$(=Di;)n=p^6g52B<)DnoV78gzkmQudR>W|{;;gcZ6COQ=d#CMG8lMCgLTF7f!v&C zqY>^j&=W$x={%vGsBe4b`)OXg6|7>dbk7Hdw?XBJEca%76yB)UIUG}$J)}3~v z_iPtJbLnR$Yi1N^3Yq7d+%}^%j+&6H8QpuqRYdaWR3syMt=H!^53H;27%=FvuG{|c z;*CExI1^fwW3}D=t$}B6YJOoRYwC<9hzbY;XaKiIDh9qe_LC*v_L$nNU`<70HEpxDlGU6-xukG*WrH)Lm*-?`}Ztd&PiCSJ@CSm%f#AS5V7$UWAt zOKSOKv!;-IblkC+4E+^3uT7Y?b-VH$fvzE4QtSmiPFgsk!!#sS2+6xS`j#%%6u6^i zq#zH8vF;n`YdzBD|FEIt`xS*hEw1`||N8&AY5#`@hj$JSuN}rxQ-;@>96EG}ToRXH zwb5sr%ho&5J9c8+|7jyp%-Y}7cfZ-&yS>+Uw8Z+F!}7Y_yv|~D$3Zx1>m&nNGXp_W ztu(`xldKuts}a#m5@=EzG&Hf`kuG=jeYKONlSd7fIM=Qcb<4V&k9lh!b2o5rrMC4Z z^YX&FJ30CtVuXCL}GXW0Fg0N=n}GGt~!4q z&PZ8x$`cl4rA&a5bUagt$ilNcI~jR`qhgtN!ljTat^rRW(%%3jpv*qFNh$^>f`I}7 z2|5ClY``L5iu>l75iFZ{crJ_*lBAgM;L0z>5_0p9k4lVFIi8Fi$(kXmm02G#{S437 zaMaYftVyE$^Z;syqu%9YyjosPj%V3pPwH|v8I3>^?+YlzG2^XK`ef)|BKJ%xYhp5x zJQg8(kH4WX=ec41^Xp4n{Us)ok!gW`zaPq^??^a}4r`_bgVhThJCzpJMDnDTFzAXs zjq=QtX=-fh-qhoJrKI|={Y5`5alGfw|7lV0Px^Pgzq@l|S9@Ov3e2FTb?esc+qaK; zp+X4~5Dehp@S5SaW9{xYyhJez-!5){uA^&zcgevr=Nlg9_dJ#fo6)&|SaY6%CTb&D zGm<|`YD7~sSQ;8r@fh4fc0Su^Yw768O<)$YxF>|g`bWVAL2tG`R6TOV#LrE|ZKqlD z)YNQ-Zr9Yr#+u9o2O_aQVTC3V>txcgieM8H)tQ`5?uDVQ#6G!@iA2mOf+>lEqIlwM zK+ITYGCP#XRd^GwVg*gSFbF8-dXr`WIu)iQ;s{d`eH2*9FO6abxd~B8rDU*Jlzn8P zz$na$=~K${)p#i7qjE`-b|huam}t11pvHNMy4>Ex9}3xzeAPAj5Zvudo7my#r~ zieW0%Sh8kVPev<)nXqrsJJPY$6xNV`K^s#GnEMQF0#6(!U=eguyIq#!@e~`HCcMSv zr*r71aveK&?gXS_`WiqGUL+EU{YONkyqRpytaGy7#5M5<0w~G15Zz?qcUlVJMrQVK z3fu%P30smGCl!VT<1hA;8`7+}0~=Hdo3fY*Pelv@1jHTVSmX1pe7oQ82UsIrP5}me zDp5>Pp6_Lcjjtw#n#xd!WX-URF;fnrn&@;)4JLYFg*9_?P50dWmo5 z#3*1O$p13d7Gn8WaB=~jwx)ie}kpo@|JUeKzUbDq$ud{nM z6*O;e>FDq9l;wLidOJSq-uk_5Q{S8J{C>~$>+7~1+SWVRhx>sw;nDs3_mgkpN3n4P zg~t&xthRG~SM?jUo{tNLJ{no`%y4l#0oW+*%&~A!VG&kP6B_H3ATpE z06y@w2q)~LDhI7b%ShqSw(`wI_QF$e0r2PQ^yyjVU3scVJCZeLYL^0GrcER4HRAUz6jE-NZE$fFu%0vYRgwSVj7dIfo+?Ti z$(l18V+I{zzy}Q=O8v!pN?}cu%wicc88?t{;3l*d`aty3&|nl4z2sm{PQl`Po?p4* z6O(Dc7+44`v$kZgrf|Bf<*TLE)bu>0Mws-Gd%&WLL+Fn;z$INsXw6mwPYiCdI$^LC` zY+rw1ePL-KDJ_-}dG?uSag+FxSp^k$fec|ybRNW4sq;>gV_)(1*Xv*KdjgI;&yqJ* zG}7bkHk*GAy4h{}NoH2bJO@Mqi@_{&sR>G8gBu+_ck&T1U zCy`4Ia*B9_07@8?j~Fh#mtr8MAj=TR6T+aF%2uXE2qeUrM-j5+|madiBX>uRUSrc7P?j5_qN6~}g#}SHPDPuezs@~br z*}tZL%l@s?Tc_8ItielBjKWVo`6QjLNTys9F=qm6g80O2)Vn~k=N%<&V^u?LOCE8} z2B-hUs-t@=w&&Xev8znmBx{EFGe$ydvYtsuMd#S_F5mSl%g!Gf@+$))EKY6o=oqHL zOc&Y_2e2n(612?PpLqxZD5NQ9(R3hTO}rTS3KpP*2SDy%B~h z*nD9L88swI;@q)~NwsWYA4#&Nq7Rw4qcv;lmoMvl_@TFQvJaa~bOi$&aEWH%eK88O zQi3`tBIwCMozCdZ?{_)-O(w#e8HY^TvZex>VcSXAgNTj>Pcz2Sft$2{c90%DNG1k# zw>4Dw@~mcFS`+r_Oh3raNeyd?h$aK`RY}BZFppY1pA=U7bxr-hwzU02SNDf~YhD`O ze00m&@wJ;bZ+`K`7wPWf)fyWbF~oJ7>RE2{$)7zny<^&ucXr_qLY{;@s4QF*`%D`m zA+>(}dg7o|B`a}~bEk5XYLU~C~(ja;DE)c$~D6golsM6&I^5%tB@A|Td;gSKn zC7yRks;cn>3Q5)s@oJEhsvcb|GDms5g++$C!K#i?llAOObHbTm4Tu8*azBdEDq+D` z?yagUcgA7Sei{8GSQF6XyJ+Asp)C%Dg)| z#I3+qVxKfIJd4VVjjCUJN|ZXgy?OIyTZBbU5D1=Kj96eFw~i?jQA~Um?kP&7>#tC88PA^I_nC6`C|+L}-W$xv@{~m831k&T6yO7v_#Nm`lBuIY*qR zL<(3FTZ}N8mbVF1i7Qx;How4F`?bB2IR*mQE;ROfhC(@68v z$Bz;v#XyU!Q%qW+zHoY3Q-CIoNc{tgl4RfLUkM7&_vF`>))cr{hBV{)qCBl+%@CJ{ zmD8LxQDY#_YO~YR#taIkLHOhrup#IPQ_`OtcjIC0qC|t zHtdrkFk$G})9Y!5co1D~ZtuVv7K3Bih(*&c-p-2dt`co=lkcLFPu`qwJ7ibH{ixEsNKftxf>Vw2D(0m~3c&iWDKMkbvb5?;lP@>D=D21kSDq1Wg3^yI$u z5)*uwy&G~vo*%D-*XB6;)DD{B@zo8o{ zdOR2f5#~W1IVb>>GPDz13o4s3ja2_dny_XZsdMp!$b^U{76hk@&~UogHN;p`Rq^yx z)vMd8x|%AOdAX+1JJw(;&DX-js27bECHa9#Wld}`C=f$L^URMj)tVj8e zKIrU!c3{)9oAw@L^3UGGhYvsf^wXrp2}$-2^%n0bwViajzU!&}PVLCa(Y9^v4xfYH zMJItWcq8#&z70?fk&0B8RES(W=*|*g}bDsXV7K~bm35g>V{@x=O(?gkfjM| zVrWQ?&9!Xz$E#~5IDhNVb2qR5g(071FSTn_0g4U8HAzqq$TZoVm95>~J-v7_I-Ky$ z8M86_|BSPQG8w5!T#yCWMe1DOCW^%J>>RG)H7Y+zay|JnreZre0rKQuM@H z-|-3eSYIRW8*{tt4y$oS=QD{-TB}?01Cz&^8Ylb*5ZCNwdWVw3h$64#&(mMj{mar|Y>G;N>|DM&l zixdydiX*EFYc78JUsm-THyBM>&Z3nCjRv!3?>gp9Ldy$kA1Uc~*lmnisu>u+dF|_q zramob9xW>JVVqIXQ_rR950inIT8__U*&}=(V>jc*P3#+a1Cc%FyMb4Ft@O}%B)kS2 z^oi%i0;qbju!X_}fH2x3X2ccT9JCd&Bp!e-1vkUqDK!TrYlbw&%=)M~Yod#Ue8fZ+ zbe+zh!kR)yX%9FL!J4=eKBiYeqcnrWSrdyuM;4hx`IacBfE5}rtcg4k8ZqNMt8PbI z?vD;ty>f7bj*qnVw_0shkhHJ2 zZ~VYS=_}=?SFGk&ZI+Yv{C5kAeppiSt8&+dJfrj6EY+#iJ*_2Hvzv5&Le-FZ)&w+J zEHI{E&&*#)R0gwv&SN?h$2PNACLEEh8RE~hWlgvt_Y=bFR1Uv_2$4SD`AUUzO+k*68bZHyw34EU;$Dz`DzJ{^pAZ z|NVyHk6ig>kXB3)L{=l+*T#vU7f z{<4Gr&t-#uX38sfS2bHaz?Rxj0x(3#pRqk+eh=1<=q6KqM068^Wb7uf36?hsk!tWq zE{|^CXg(q{fFq_Iu@U{jkMLaqG>tK3{e$;WXk$(q4> zl5zXVU`;wzXg1y&Cqb%F8AJ?gk}iiel_Maj&*y)1$^NXYoh)w>hk7OTlJv4B(nDfM zlHcerGVSfppX#aD(2@Vv+SMO#(!aT_q`%Q?N#+|4lPX(oo($Gxq#(9Cp@`@=(rSCf zX@AFM-DfxE1)Yk$K2ph=VvJ^j5l!(E(R|dN9w~q6tft+EMG?GY%@CKSF>3}yuOpG2 z<78-xG9g-T$XRvc<_~V&`U_owUs-N5N;M^L%BVBjt=>}i@Qz!yeztPshvS=f3_tn4 zzWw0R;>K&&efo%R2t%gw{w#6NYVFV@jRmDcLXzo#CPx(9B=8v`Sn-h{C@QCB z47!MM0-9JV{t~UADG5^Kl*$JVE{3^g2sJn1a`y2}!jCJF&fvmn*_W&tR%bHhU@};f z?i5Y}bqrxmD3fVG4A7(-#920uxZS0<-}d^l$KEvbL7{?xZV(UYF(Z-L(DV3m^aYhc^9g{rc%mTeesG8y_xg`s(B_9x57B&zkgL z!AjOSA~4A+n~08v!|TgQrt|(uCAvug^eSw0lZiyoDsdc@zZx#&=7Kosya9{sW4>ry zcxRP25ygbR7NawXo8r4gauU18$Cz%Fq{5OlLmFddebk&a(MY;J1T0ZR6dlbbmWgh0 z9CBix)FevJM;Z(Z&R_UhMhATl8K@%~Q3V+d6%fK7pjLW_xFQQN3??-^h#a zzIf!sk-C=}IzQ?uX)6&XTYNh*F-==6FWUp@^(|IIaC$xX`PA*FhBaA?n#HBH0!@Ay zc7qWkfF`jMED17E_jA)wO0s5%KT8U0w(Yon>u<9w27(jidXq(GaV9yac}k=7xhuS7 zmmm7;^LGD#Hw^r&slhirJ>A~kX7dylwN0!t#sIstN9KvL+&ya*G2jyCi}V;=PV<4` z_^aS1FGfeTSn@qYyrQsr))Y-b5GQY$;!^>x@t%K zRgHmnKCu=F<+)E_9xGQ34IVvq^w=}UHt*iF=gB=?z1@tX5|xNR>^6JNXs!2+BIozr zWz*#rM?y-C#KoBvuZ4lE#wAh;^Ag3pT+``HADQF_N+-eBTDMmsIKqTG?Xwsv@3zH4!RFq$VX~K44 zsn{V@gm0pe^1!rHcA{LE%2SdxLmFddeM|;x(tlz@vqN&|=rqSsS;3n0u1G}UfJNXl zl}f4dyVA&-^jvYY$ch~tx!_LPIWmNS=vI2|`aK=`{r!&O{EWz7s1#{%F@8?stcj&2 z*~EP`#2p?C;qzV}f{`rR%xKG#Cr`3Z6qCd{M_@;cED=p`ldL+nn@eC#-eSjlEd8zi z_TKiQ5?^siG5szI6Tjy|F|~-*;Cag>ciXjd*YnRmKRh(7gccYtLDb*ra#pXc9(`+U z^MTC-4}v8Y>wZ#M6UnmxwdSD7ZxpPF+)(=rNzX-+ykyM~f0hi^bU5t(+8VPtZ5?YC z*fbQBmTmv;@Z)bZ)cfgPVgvv$W+u?^Bk9J><1BT=5_n8DU%q@fX><|Y1Sla(Ad;qp zDc~=Oa-_=8OeU?vqZAW!)j605Yl`O)ewn-h&IN;mkEV&ins^ot@&L5HnLkPiYJ#Lh zL$P(BDt0eAq0W>*QjbX13~7v+^)VT&DQ2ai#B9NFO$Z6s%Msy0QD5;&l>{AX^3*qt ztO+?052n+}ufsa9gCRgr2=LTvd7FJs-NCNhXGWbh#f})PUGjt#`<=09iL)kgHEt4X zQYrewayKk5)6ba?M0bdHA0Ih#1hEr?hc8)}ogMy?TPM5`2+aF@Ttd}Qm|}P!Vwsq4 zyb;XGfKIT9sq2&{x(REZIB{ZX^Hl%w!41FOJowU(tH^~fqkhS@Q^km-xvlx=3rC+l z{wz8$)9y!q_~5h2V@*Jl=?nMWcb|rcuRJ+F8GnXxKqmYI<#-0}NY)JTXUSkq0Eotg z8BCV~SgjV?cwJNbv(LXsj}arl02|sSzAUCkVond~7buhHChH&Z_u6Z(g(i7#5PZqT z5!@mM4W>k}ASSJhY&$7+S{z3bXHD_I2otH&%5cgMCm-4PMc^YFwhNk6EDt-_L*SBY zh5M$kNTZ+g^H04JN&V>6bFS6mgJjK+#+X?jlfjx|0x7*EG%}bq@xgo~R)G&jwFqb8 zm@y)#wHBJD{XNpin)DZOv=}%cD3ArAAv%r_31;);xjJf#>PnneOGZ26D7AtKOOQ3O zzvP%SfB!Pz2sOf*q{!I;D4%`yS;EQe5YiyJ31||hWU3Gx2|=RO7;a&OIZMERt1#T$ z2aC*A93hYiWpdU2{rgXT`}Ord*jV@LCjTEAYk%KhuX0qESBtSzpcPSnD*Bmcp5Ybh z3ATc+32QENW3!^8{R zYHAfShHpzFYhnUmO#$VYTG}>t4PgQ_f_2!btSC=tmU0Y|NJ>$3AaT|t&7@`pkeGe| zq9*H243hakbcf(Y0Fl?Av)Yjub_wef*g%1)3m_6AM6m%Qd;rlnqL_FiY%#Wv04HHl zjsTimOQqqf_P)BW=KVV72cEo-y%j&G?p)VZSzampmS`r___1Thn1{rDNDjpG@WF+X z!J0Tf1})$( z#-nq|*T4RC3TVZeLcs-bLX-R)44nvGf|ZyZ$dX@*af7oFexOyJDu8vOaA<@mD{J+q zpBjGkE8bbDuIa>3it$M-YBOuZRI+ABW6Z3Nny{u$=gi6R>vT|KpreB>3Qh`PO}a|K ztVv%A*PDndjX_)EDw;;t6ueIJ244V8I-2yY`77c*G=AJLa*pd~Wy(eTy3AxvM2&AF zkjZ=?0FhuMiyskaB$$EZvA-n5h5aS8$w3NWg)zREY%U}qhi)^H1pP$x!2@10c+7z&;Zz{Ml;OEG{PVZ(yqeZ)v1(VYf5+2MG|J#kb8U0s&- z>qCym^ikCBsVWW(+UAk?I>zDUo;b1UsizFHy?S1b zbRK%HK6jUA)1bB6Zd5hpPO@f*YH7)usQoF;r%}+X%qIN#?1Pt z1#9Y-KHA}QyjoPW(O?J+#6)B8m#7YEgx-)$6uKqmOpr7uUC2R#J@`~kMOUCSo=q2P z8VtI!5^qgqV6Yku9$_L<=#%yj%@9i?Ez8F=N=B2f=GSE)Yw}b`n}L`N%>*LF`kG|V zSz;4jB+Q8ggdG7&;>rX($g=~Z)C;{P#$|$-bP4#2tz?H@0o2G}!XY9=3a4&3va$4y z3YL6#yqo8I#p7R7=dcGF4quH=C3VGLz7dy(&E-}&r}zX;1JsH&1!&?IdC2%4M*USD zQ{E=e#Ief9I|^H@ruc^tsQ*T#q|TrlarO7*ciPRWhMh^)4Dn~0v8Fz-($I>6TGr8| z??*$h=$2o7@E5mi{#kBe!`VgMm6}V=&I(WYx2E1R)=gO~=1PBK{rHZq$sG)v!7TWS zig<;lL7zjzRr%$>5CKbKnIzvC`NMMVgfrPki^z|YQfIIxPKRd8SvCL;YXFkMoy0LfFwwQ00Cwa zBtc4~NKq7(thKGMewOXkdS&hNu~t6z+dcc<`wfb`yu1@Jm~bUepE-BBtE;Q4tH1C6 z{p^L)svJvo5`LE} z=h&Ucx-RyVe6GCs3+4G|iUxNK6&8$6Ki9m1<|0|j31dSM)=?|F&|EY4ROXIodh*PS z%-qbJ$d%a!k&H+~b$wG!qmL-fuE@_93wG}`h* zY}>jeJHOyFHOqg$_e4s@bi0CRA~uFoilK{-voH3I%M>fnZswSq9)wVS{No>Y%J5Cu z4CM@?LM49igCF!8daz&zN-zxo2R{=S;TlxFhH(pE61Ew)6WgsoeQ8hzh1`g-#t31N zQ*H?c3f_XKTr2WmR4o z!0?Xr@eUTbW}Gr6*5RyhP4AtblJMb|_PK=!W$5?Ba!uVA@n0fKB6Wtv#|*;Uj7LM0~Ggyup;804rA za_3Gv_uO+2>S>1y(=Q(BjX+^COse5N;m1xOY3M#N0HSR2m;s;=p*OP% za_sc&!_Tbh{7O&Jx{|)ZK6ms&LkWJNv-_f|yw@1&G;3bTStOQ(RY zGOtKxoD#MmZ4{ii@;d+=K*TQS1KTtgVkglltPb;|Qz%`u-2xL0ztP0l9j8PfnR54H ztT~2)Zz$LuzkyJ?1*mi{;T=e&s|@AT1n<;mmPbxolug5H#U67Jkzh)~mJm>R-jb6^ z{wv1H3vcVmBw1(o)IxF1f}HeKUDd-qO{SS^rLL}S?P+tks$t3Fx7KVKYF@a7jc2Wm zz1IZfn(&bri(ey`oRPeeVALdZqKg7bL?ZFU(BmI{DH$2u%!EI9po2)W1SqDnYzZdH zmuX56T?od&kWGujAG!U=gAbqW?CHAV5vXG`+}GXpC^%Ey1!TLUiS1B(W{s|H*T^;J zfoOWP?2N38npx&AH#fDlwlccSO-(CT^t3fr+po$`IofmM3%k+}trT9L*KusK+purF*+k@; zWRg|GHF+Y#WFQf9Yq=jfbO_XfR78-+5q(543A28b4*k%Iq0aWs+L~IZ?Q$T_{Rz-H zEk+%gM~;{w6y!h5G_qi)!q1;SA66^}*)D|OoDjICYuyDmRTK?!p|dl11(fj`xu(n6 zxf$I0YA-1%E@9IGAh3VYAj#F1bM5)PQ0^Hu0a;U)w#YT(?Ah#a&D0d`ZLEi50;ge6 zsFD~0?U?e7%uxn}tg)^Ak2|*h)C2$DPw)NT?;U(SE4L(r%QNHZ6q9SyL?A@dLYs)= zyWaIK-b&6+)#k{w9}=}Hy2(rO>wN7y>lS$kne`GZ?Z8msfG3R3PINP1{v zPG4hwR%8}>jnirWxyu8I63(xgoSq-a?_Ac&87Yd5P{LV$O_R`Vz*i6)JLGgkJt1;{ z)Ut;1z6Y0YdVWju>Xy#lPVP()2^{$k$h^ysA3turOb~27++BCwAes}iuVT= zG2HBqQ>;1mi9}Kd)KdzUDVub^PDQIhbM3oDsDt__5IibPN;G|J4W~?jhwjB1y2X^4 z7rADfGA7pHEOJc(P3|#EYDpYXNkYa_61X63M380WF_K8o3A9V56d~9(8{5!gBI(D5a*hn<)Rc1TrnR*+wKg|W-&uXo$qI9;tgGv~PgYv&IZ}~W zTr(xTcvETDU-hl~%7($+!yTO+-U8BzEL!MHDSx1H*?;fq z|A)cNFK_MH+?!i8+NTGOa_Z&#<<~?ji9N1q=;y_Y7ZnA*c}$U4il7hXUKEEtz%|iO z@ERgttcW_W5V1`+&HY5?n%q_v81fgufl&b+!8q|2hYspAA9HrI0VQUVJ=S_tmb}O{ zqFJrS+@QBU#=B9wQ(|K^B;6T2QmF@)Mh${Xh5rQ`Yv+=H^}X z$QDdd`;Zy)fw(ZOtjYdzSo-DWw7e0bX+tahqL_hha&S7!7}>Zs@kP*qf$=N``3%u; zKoL}l+&V>}Xp>VKP=3|~YqTFMGCYyTC9y&t_7$S=a4m?#8Uy$^tNq!L3yWuYd`0r^X&Ga?LnpOsvCMPd7Oh@?WLwkD%!5u4GbDcuBfBxrx{#ss`AP6X#&SSInBrERB7wMOT6*rf z{QCdg-2MObZ~m{_Zh!fXop+4R&cXB0N)Aqx(Naf9AsIe#;smlto-w6JcO>szVfmM< zmVL8x`0}dm+j|Qe3IzbYEJ1coP!Q~LOTj~J@pPn9=v+|>4_uRMvjny%p>!5@d)ICX zruh|=HbrZWcba)fOur_g*`D9_SjV|LT5mJ`T-}3Z0wCx+yF&8kQ^;c*&wbfZ{AN17 zl&(u z)(+f$=kea&zN-~cvTd>;vqGJbIo!zLOI}Pdbb)gG((rB^RNNdH!3QkkE0BbCJv?f|IxPSJk+ z4rh^Tk_-YtEJ%_aQbNRWN|JX)*3(K$D>rZ6sf%T4BxIDH#7fmf`-GkoG-`$?$~G72 z3&=I$MQ3Lhi>qr$3B%J-dk8Ymw7an+`TpS>i}QqGX1R?$R|0X(!t|`2Ic0BDHvPA@ z?cdvd?-$Q(zjMc`HLG|cF+$FGJNPC43L?03*UqimwpCVC;+p9Lk;s|s>?iVypDt@Y z)7pMpM{!HB?`OeWJ$@cgy#;>F3l}cHT;PkRzWCycW)3MztUQcDgQw0CbxrKoDPWCj zO2l~VQ>$2Lp5~ii3=)=W_T_gy+5FJq`dbTgMkfe*OxzuAaO-+laz4#xoQrI#fo?8x z%^BFUS>c);ot+sOSG;s-X*X6a`;735lp1V?l!mZPZ>&UYT*vx)xr!FPlpTxkck)Cn+iUz=6o_-RW7Qzvc`ScK$AzU9QPHU2F zy4Tj%4b|%c)vm3tdbGCirPAu>>e}vYZ`|3OS3OD@Nh~49Q2kmg^T{WlyJ<(p4fuIVuga`LyL9rKTQGXu7<;s^%yRiah-M&jC zdMNh|9XueQKzCOTHd?=FQ}TDdQ+nixD#aEx?*1azoPom5-zBrlHAw&)Nth^M;F|Vn z0(nYKF8saUee9zj{$_gmnT(9;nwn}FHLk?OB@#OlKhFd!)M25xW?^>f{>~+zKU}(R zLw8}}n>q$TG$!h)G2@zOP7gH#XqXGXvIj{ZuF17Flv8xMwCqcjtG}@J)N`jd?cc<& zDg14@B5NMuh~nS}?l{1CS@oalI=6F#pdj~Y#!6;Z;8w`85n&hj+5{>6r6Z#1P+pb7}I~uScCJZr zbnU!?QJ_mp{oTRa{*ROY<72n}S!(aG^yz)fY1ZXI@cE{~hTm&FCbol430CML*3{MY z4h=8W|5MNDZLrj2ApFvLnfMv`52B7lqLLr|=ttq9{owu-G}rzns&j2x&cceDSg9eN zLhUq#Q&7+Y*ED4ck_6HjXsCUB5p_f0G$Sz%%ujnpw{h$1EX)B%fXg_`@#WvFjN5Oo z+`r#EBZa7{?zC4HEl?qWaZ+vg<1l(EcE?>t*<@E&;Hk0M>_Y>aizAJV*)P4+c;LWv z4A3IioPom5-zBrlHAxBUNYF9Pphdx)TO4w(Z;SMKlrr+kasH#$6k0?x@SXR519tl_U3UC~Yo- zwzzm>OJhZKMN4Z-^aT-J?ZyMaA&RO#@x&9@DkJKXmB=fA=GQbi9qFVpVoy$mYpI5> zS=wh&q<0?au70_$;E72~Ta4PG(Z(=l(0<2B=K z)wOd?ubxIktwUOB%FmO}6 zWRlx7i-Mx~pRt&}NaBw31 zl3CnYD6UDtsA%n#?^0^R@I(*(}A5ut{G?5 zuAOUQ3@8!t2=A9t&=gz>`UN^Nckg}5l4&KoX2zZgIN9|*AM8H!@v5yj=#&?R3?7X| zGtC@*)NbYjk$G|RxEZJ%pK|hLT5c@RO_{Cnj-Dc*W(Wm@p+3kQ@WIuHD^3|DcG@b| zn~YS-Y?QN~5C8K;bibvWx>lc4a!o7`Z~#%SF9Ll-{z zi8nGb)(BCCkO9H6qk9w;sYyf-;>aAuow%Gq<>C@&@o)jTCf%c-o;^H{CuN2m&h;aY zZ9VX06y0=GeeAW9r+Ajc@@pE3o}0^ydH?o_b4q+S2hdj-O@`O|f!7VSP<5Ogs{CWM$0z3CkNr8x!3W zI=}9^>%`oBAw8t)H=0zW5rRR~ILM_wD!Tx7z=gJAHDquLV4~4fc>Joom8uY#fxN3U zJG#kBiiT1{j8mC5J4}ESo>7m_cH^V!fNnytw@MoQKd#zJq}t{IA;*(5PDImP(Ahwm zRV+U*Z^zD^Uj1|e9gAEuDnE(1GwWQFeAL%BT;ZB^DJeOB_y>1i|Cwha87oYdAhl#p z*RG?ZOTWt?m{>8rBB_~6ZthD>sgXIHVtaUu#$}~Tmsb7pA3dCsY_#~>UiY{Hukmo9 zxF$`b@Klaghex2*dZM;#e4-g#jt0^HiRk_e3XEO?gA$EvYGF>t3lCoy+P^aY!J^u~ zYHa&q=fIDKyT8>}`?ZF~|I)hbN8LT&UB2nXEvxTZQ_@(%m&p*aj-JKl#lijPcmoE2 zzhTwLs?CpW-TyZSw|{m=`%uT~RjbV{urfTDrV+fJB37Cgh`XGqFKb`6e&hPgtWkZ* zYsk+$^%mFU*BpraxcfEha%wi0k1Q)}VRf_*1=nstBv?m{$%?a=(Zr~<%;!!;r0jUX zq2|nnNc!8$vW#1SMXniV)vlduVm#5FEe?d=pc>=>3gs@u%6GBvJ#bCEMB0jhU9FVL zZ%U9tXu7pFEosD0$$x1k&<8*GK~&O+%xHV^dK$ZF77>1lZiZ7lpz>pWN?;Te2%iB6 zc1V3R5wb|F5J#t`NjE*9%sP0KQQtVYfuz`2b6a4dO7kx8MK8!G0(H;=I?@QPeYoco zW9{8}z~B&#Z94ZTyYkJUDH*5AW62n9_44I;=g#$9xUlioTa%Vf&FL4pW>k0*ac9=K zCh^M8FKKFAU0Slt(B6^~&iv-w+|ln-Y%i!P%sW|-`$R#`6ZzSXer*MS2Gq1rcBuB5&>y423R3&rRS5h+#-$C??}XeFnn{3a5io1CCw#yWcH60M|V0x4IlKCMGL z>c)0JYG49lZ_{joM}YZ zdsLaqnwq>FJJL^{#6zK?vwm^qaIbOi7_k=prcG5p_(9b_{6ohx&m`Y;(?pezrY~~M ztJT(d%|8cRGxal{S@nV6e&dGET+YlKb%v8so*vji3X6*JKlzEBAO6rA$w?1nMod9{ z2ThDbs;~d_y{Rc10}a)k(qq$|iV9QgPDx44%{T8%O{G!qC?qz|HSRBzUz7Kj+5@!! zxPEE14H>$|Cz`?K&x(DRQg&)+lw8W9@)I{p-uHe4fPFgF0&Ax)C(F7u3h;?rIhNb>aIJwt6!)q zezm;(t5w}^^i~hltT;T>{Km4uFAgtT-`@Uo*RmgURX8guE7(HYnonBm0hvDY12SUKZ%Otnha-8 zcTZn$UrzR$WA)-5XFd;8)6F@a8dw(nnsMgp8o4G73T}Sy8b0uuVM`%Udt*h*&)xI) zw+!6oV?y&O39IJ#-~rnc5L#v@h;wG~s8n=|@J zNi)-Hj@0E{C^XtK>(Sh{i|tjdm34>f^Dh<+o*QaB-IV`$QNar(1?P(*RgsK>jO%qzu!AeS)z}KFJC})ZCy)y7YjAb3qv-S8|jDz5^v8)J-If`L+7ew zYYfm7H@7j${F-Q}&^h)gCup)!8GtDza5B6Q|5G4s;>|(Ml+5D@k#u*(2N+!YZjH%F zi9@PTB{f0y&{HdBXzGdd&Mn>yZDnMn+YIt7tciaknVHoc9g*JNkmW5B znyMucR$djR->>?kysB;UAZ@L!YX%4TZ+G2wSJI9hOHQ4#6z9Y@NnhleamtujhjYL+ zt?8OwT)c)>Mk4uyi8%>=P2%qGikrPIph>LU$V1eEZiI9?K>no1W{d9ArD+O!^pdOH zSrD$7k)GC3n}7Spw&AY2FrXDe!4z0?we_Es{JQTd5KE_KrC5c)wZ!3?D#LSZVvqrp zRw>%GZ`ZmDBXzGeR(!3d>DAWZODnrS*H`)FnyRnXH9gU~`rftO+j~}STp4D_x{w(N zO{5&SEE*mu&aCULuRqb4|432hqdETCAL?u!Xj^@HZR3kA-4}bSAE?VdpNEDE5MRV8 zX}2jaoSH}yc;|g&AElsT5_vswO;L4BRI6I>NaWq+3@C&F%QoPECNhO6nsf!CZdjVq zoMJWPRHmx}0R_%_YQM~iXd ziKw|-GxX}OG71b#RGqn3O#I+TzVo4nvd^5k@zNy>eUdvaa?LnpOsvDX;F_e9$AUjh zKZ#?!veHg{a&n$Ee9%YK)7dzh=n@)2H)7MnaN{6K=Ho^;AdMvPG_$SaxFCwYe6qnUY=j8%MtUo45bxrS-!}#kIeB?9cvi$LCY>tA>ZK zaLu%Zc6DcD6jfBN7+&4b+?>38&%4*Xc4LldtXHuiLvrJG?3W>!R>~AH7a9MBU-$)` z7PEzLP+t`?EgdTrQkOw*9QUI5u9bp`y4D66P{82IK{TyyVWnQ=k6MLEhfJ+~bPa$* zDuz{lDY0~u>dByMjnBne-q=T?t;q4)R(R>sE{gx|IiUI1z%|J#w>rr7>2uyq z%eGq%bKl;5Cr_R{aq@&Ein_bHjh0OOTCq^*OYy^YUG*qx88Q^@w<*caQ_3RPM#XxKkVa{Wv^D{9Vo~s zj$~wJq8q#&yVB{RP_zkEYHdGLDV@F_USa3AI81nro1c z(&H39uLWzs2>)UyldZC5Uz>qHPuxP_qOC^M^e(# z+sl$3Se~6^r##9q#-VCqk$kDbmp1HYpD@-j;X=yhW)xLM_(;w@&C$DpL zRz-`dO?}lna~P3{N1~Oc`*6n)ezwd^AUx6lO#?T%Hl5<~{I~!1-)dMXVL5k<^S6HM zw|s5Lj-}u!4sc*bH48&2Hm#L}5XA;VJROb&J_@yrK(KF3Ux9KkB2Ty$wxFR_FH#zk zH*O0{*VpwM`y5sN2vR#;HrBS_G4 z{e}%5FfMc4;Ud?JQ^v$PoFlG@2$IO6BE1vij$6CBdh_yzKK-eu(o%10Y3bEvi4{BA zqWgtbMm-#4NCt{Qykc{P^x!m&Q-Cl0ds9hTRwNZ#=`)OrL=zP?&2(=zLAa)xfMA0!d;5BK+_B@x z*N^W1>qB?EcKoh0$IqU}3D4@3!)3p^-7*^m6@+t~CoRl~?8)+(AEEH9$8x)$?pyu9 z+U;j{p1A*RtkH+jIZEvuIdbH&#~y?JsHc)`-gr({_VQx`8@{k{_4&1{HjcV3m=ia} zaaB*}9_f!$hjA(pljN->pkGr32^*#^VfmX{VX* zE^^H{dv=XnlM{6_DBd$qWT3VKs6tBw9?{07J?(gr*%~}5^AAd40_f&iPP@K0Cvd|4u z9xFyfpGA*LltIaJ$9`ffV1GflW+XED88kL{x|$hR>8m`<&P>b8PLE`~x$*^azp`%W z>iXm@Elcj~yQwHUsjIsi23=*T)5`7v6NGE3F7C*AS=n9H^?XnFKlS(hzk|EKxA&n} z&Rw{4;m+fC5=AT%QX5VLc>y;l2cY9TXGVJF*6f@|^K+jr@Y(d1N0zO=cg@}h_Z>Sm z`VDlP{I&3h{l`QXQ^}^d~IW z)NbfCa_=|FHFZIW{p#AGXId|uZn>j0uViXB$T%-xUXvENW}H3y3FewXux>7>{c!t< z>q=M5z^@7Mcrk$<2dCHws|V^4!7wTjo}iw7~>7LPB$fhD!g(|0uq6DRZizc4#qxNzIiuU5h4PW#y7fmu|Xn0VX!DSh4Cm z-?{GTr_*n|adO>WYin06x#>$ONzXPntzk8LdiuJ$HZ?a7G&D4l z-KfxzE0V2W6=y_g%eY*V#0wJ;{b&hux3eHzv#=nqwW*=H@*Tqr=Ij~MMjDr%T9eya znGH$6L4IEDzRp`l8gCh>N#5R(UXYanZhqpqCVYp;fID+ItL*j4hVQks{it*M-|ag6 z>OJ>8d*9LHM{Db9aXwsAHwkLX38TkvmejPY{_KK_#l^3b6@Q_8==qhqFYGz^(4nIz zjv<;pv$1*S=8bzc-g)xQ2OoS8<3v3%*I+HME@P);3#1nA3sg}S@h?GV*HVf&HJMy6 z5}9jOmRGJH9O-H9j=U8v*R~$8wY+&x^|pBv9*Q{@-FP~6cG?i*-mxV6%3v5hYsosDxa#by|1 z-)Aa-!C!3IZ9Nhh-UZE@+*470m4|6G%fw%6nw zTwUGUe08g7%J4nfn$I^ z_MxPh<&(d+usO$7Rh8ACVqrzNlHQv2gC^*`5M z+N$z|fjRVm8rJCT=??`P_rd2aagl4r*|TfsnsJUgzU}UT;moXsTiLVm_>26PzCvk2x|~)!`lVm`rSPeAiY%u~Xa^q2XhI>35O8hZ zzMYej7nAOALxyNLD=CEy1#SW zvEc65eBeOmcfUJy;X?oF)ll(G4tk|zIrZDw3y*__Ld&qxYY3*aI2!tno5mfa0g2Jg zYSW-kogKzyi91~6nsLgQSch}LHFZT&QjGnl1}k{tdq)@l>fxCW!bTni$-49qqqGw%PY&r1d5uet%`=Pp^UpPNB59kNlb+g^wW+80tpW10_(1VRY2Etr z+;uD3%ZdvHNFWi6;5J6+K}K6!8=?bEezLhHSu%NiR6e`gn~pT+JXKKia#_VUYF7N| z@UhR{{@~>Y_usX@tGAnP09b1ZAdVfvzKOV|R?f)E$nP)Md3M*j10(CUuG@cbzhRn3 z?l{tQzNh@g$n%dp`|PuPmeC$REii3JsGab$8NQiL+Lh+k-U~N_bZ;d7 zElzx#$Xv6(p!=oPr!IFtHe9nJD`WJVgp8sV*1XsT+CA}OCD55gt{G>~u90hc_4rCG zBe}V-BC_e;^y1pl31dIzn;w+-xXlnulgiBj!ZJ}$Qe*@t$dw{)?icUm<3ye~HBBe- zxge8=4BzAvr6!DKzyVrg(_&(S!3BfcImAb)67;|_%?*O##%1c(LOEc3OzE?Iyi_kdJ(bjf>w@p0@KpY-gn>N6{^Zc1h0^rf-IC24^}3m;$7ln zvPpqyP6Zz9G2JWH5gRyY?2V%D*+d~x|ATkjA%8S+qMb#q8K;bibvWx>(@2s}eRB5= zpE+M&$3;J6*az-H?96u~`pcH--*nt`)4G(DMl%Em4Y?=ianeKhf@_B&#h!8~96ueg zi@j>b`}5B=(=Fs)le@F8q_*@bD8-B)(1n`RD?po|4B!9vW_AvC?%cJrxuLmuu;k58O1@Dw^!mzU&)t6J>HBXzaBFu@H<)G! zwTV_DpM+LGYNAJz4)jI6K|J|K6V2>7eD~qz2V1f)=2_Rh|AFPtymaZ2M;`H|BCQ?D zVIdA2I3P12x34$vUAPy&h4%ulIUy7~$xY)i^UU3a!0ZY~1_qjHG8 zi|%=F?d&k_SrYSbk!!};vuos<1faCEB)PCWCAT=;I3F@@AE>MovcSxx*>1}@L{zW2TM4b3zk2tsmj3a?w*o!1lT*ms&YdjpngFaOtXWhx)w}9HYHTudTae(|MkF$ z6Qvauph?M5cBpV>f>Ahq`ZO-lV{gaO$yYYt04fO|mdsl>R z6WeLz86j(JZAwi!`O%MjIce#w85wUbMLU)|>i3XEk{74sB$z=W?s4gJbU6Q96FTU# zYNmOMA-WNi0R#{P5YT^{H)69>Q4|^Ks?X2Oo(t3OVHcchMk+F^PF8RH^5)Hlwv<$q zwhy+id}?*Y7i${6(cJ&yie2aS+RA0^x$I;KJ?H-=p$)Yj@E$nxaOflhXUveo$CfbUcOHZ%s_te zGVf)Qo5r0=WUeVfR*+xN+|cAJES_D>wI{R3AWh;hj(flbKV0ORaVGEDxaO5_Zk6PH zVC!R_Ty-Wj-+GD}Hum4~F>-x`sKK+xQobu%_`ZeQ} zF|iJ(&o$H1#JS$F&ejC)ULUKzxoK5%^Js5LM~cNIsk|~|&N!3I%(b8X^nEGGD>E{3 z7;kDr?Ba@IY)BqKj7_IAM38_6%M^QRj`ruDYf?fwM)BtD8Qln=fGw>JjG&1MfDDJd zT`k84Q;zrFTwXAW3+iUeh(wBWQ}eS0H?FnS-Fkt!W_Ehcro7s3H}-vZpzqxB%D$@h zW$kNruW7w+S=;6IuKRm7AK7yDp|d9IKz5{%4?shCY$Noi{?$yS1XVzbM{KC0&=O{>^kIF=6h1w0-^a>2t4ACwx1|Nm9x3Levxa&*|Tfmnz!VaHgzv=C@=brL$ChPrc0?sb)#`} z{gBjKX12i4T)z1kmL2G(;g}qpT$@~&T$`lG%I!9^k}jZ|*ryn}6!XxH;6yPkfn@(9 zANdIF5U^%CDNDfWP!$(Ch1=01gfGsjva>Qfi)gZ~;c|2kU!-v;rfa3M)Qs$gjT^n4-!uVNhD{CefF}ar?;%k8t-Pqm1JaOqoiG(gT=)rSiGY^0;db52S(sx6`G)v zQL-MJIH&vb&ow=rPToBGb>M{;6grm}m5!!XHCX4==Gv#Xrrt3aX{atKy%L2{v7(Z) z;{!`p)+gn>Ujjb$D8cQ&pbot~*vv}N;_m4m}won1s% zw8$7r-6Wm>lo2|sHeLu)1Mi&V$+aqXKBvBs_zyhrfR7K2E@=1c#x1XH+jDZyBac4v z+;h(XXo9T}4rZB6PIpr!b8w2k+cBu$-QCWPP}ENn*YwnEl1C%r zd#JMM(Uz)_nu>-Ba=T;ujx%S@V0GNRXy3tu2MxQ#LG_v-yGHQT03C1x$H`lO3Rt$C zN$@SAr=NKmK-->s_PI+>Us^x1o>TMEvzNT?Am?_B)HEMR56z9BGZc0^&<}_qJIJ<$ z3QT_A=vl@fVaIERN(O-^S9UbM0G~#S)<5xg7P)4eJ)1GtOvx?z_d70U9(lRAe%a)d zBJrhSq8d_O=<1iWlX<{v%DIj!B*7P5MPYeZ5H*L+%A+ZeQ8?+>jH2K9o!@au;A%h# zM8GA8p#&u>Hf-3Nv+Rg+SWKIJPDgG zTWyeNX(=WfdyDA)BG-&l#>6_D9@k7yNyZ5G=H!eHvW&jkiFVS`(n&32%*qZ7?C9w^ zxop{{k&*RTnU|B7zS!2fqokzx`p>NX*hgPZPP$*vIQ<=d&D5K2YOSf-mzQVg@taO{ zH%d;T7=1-WqlQmX}sp7|DL~LvW*Kpbu>aMs}c; z6u(0&mhaxSseW|W!#f-d0_eVGbnWk9;khQsOG~R;R^Pp*C!-*vqNj4zo>d2LKX~l+ zV~`p?4h#kKE|*4#1l_E zclo(pM|Pcg?*4~gxKLGF1$-3?TaC*!SkqlBdxWH7nXq0Yohrd!gFd-jf5Nz?R?>$I zGS9#mrN9VD{BhY9xn`U_n=#k4V&&5Onx^IJ%PZfW_44B)2Ggu3O$QzN?IP)XmPT!w za{g<-_G?CMVw1jIDWuMYX=tVhJ7y`o&IXaOU;gD^7G4jrbtM26TEoZ!ZTw8k(AaJ} z&>M{Cn!*r=cqD#?v4!|GW+?+-WAJXu$=vARxg6Al(tr%KkyB?I`NIX#&`}p-bPUxM z+&46cwobE45bdH*)Jo7V%0!V-#k#2nR8NZ@`&i}j)29cwZL=JF$L7r$_uZGYX3glx ziZR?(vXs3g}l*)OWNBF{y3eL`4`R2 z7gw#?7|D1nH|P1fx-~jmR+i1m+MkxzZ`9`7*N^Vq@{tdp|LBLmS&)CYtgOx`6Mega zg5pnp;?}}~otcrTneIDFyQZUg=9*Maqp`ab@0#j8>BYI3X$Lx&9ABMRSN7&;1n-nq zgDtjax5mzy0LxmtVO2 z=&O%z`^t`W|82vbZ|^N>E=f%pT}ed8Y&62Ye!Z5*HQ5O)6U0M*ps%}c6J+;xwA-X_ z`0bNkT!Q;GJxeeuTs6U;F|I4+?MF(my+y7WXU}HNHLtwcChLulSpqM?-ucX{gIr>n zCYvLg+>;Km$uMdYx#Y7nTgcE%bkkSS%{>=V2Q^N;>s{|MUeo*`BRDb6sDXe6)F*f) zsSf~;bw&bn?nzveO+h!oh|EAtFB<7YF-5VUA1Xvou7P)4e-u)OHPMd3v&dZsS2HkMOvXB4KQ%Sd+%E}_0?Y+H2-Q9a@sxEbP-qYJV zxOVNPb?dg();8$4koD41Uqz26RmQ@7=DNFXzUjsG_G2R>n@B&E=Y;98IbgRHg# zN~TY4Cc5dk>55%&uBoYY;@981q3DIumjAnb)%Vx!f9>G@GY2s5lA+SJFL$*4V`txg z8`$~k?v*=M<>bHFFLD((fR4c=Et63cDLhbI_l?G;@3*!7Lr43Mx|hE?cS5;+h%hC5`QQWz{C3T>Gn;&k|BHeJZ^? zDQgN>2*(@5!yPe5q!XdEk5F=L@?s*9hGtsZ2&d%lbQb$GrW1iPloQts+3?g@1kg!C z1i^xSDUT+0NSlyCY>%DA&mc+gBLJ3YR742@;hA;Uh9D$wrJVMagGz!ptQ2|W`;?h! zP^@1W4j(>@p0XOi6T!7H=wU+=+(@yUuMkpZLxTbb4Lmil(ou@~)^>%dzPY$kl&Y#~ zcqq?hKv#8Vr}>hpWo0S8YFjw|7sVF+nsLgQSclW+nk1MA#!C5EWxo05s??O@`S}NK zxPD_{!MUEE!-Fe^ckbM_Z{J=?i;tWrKYBQ z>XSQfxL)^YoJ+ZM^ z0&Z+l#uhRwDQ>jjTodSH*t?&&HS?+5>c6OK`OnKnzOZ4}J-eGbnlo2r6~0hX^%r$r z-|F4}g##P!+*D9o5Qcl`rc;N>xn}OhywVpc%D+-o@%5VOZ`RfRWn=&AD~>;UV&|pZ zm6vLBp32X=R9N&}>9P&W^7Hb&cVR5uDk?@(?0X${a8TZb{^k86JJ+`@Z@YExtv`8O z6S*ucD-F-99p+T{XGLyf3t8lvarW$5xMp&G$tO0SyLrWJX?gL3_+#5V;r7!~uP|D$|eqq zmC_L&&7b|*pEZ|A2NDQG%Y;Sv9IJ|1+Cq38J4E@wfLN~cp+PC z)R3N@{;h5;WV7&lpp{h8?ffd4;dTpm)GJ16qovVVoAl_W!ePTBAs8G}69uZ0qOe$f zOBOCX7cdXrBUh=j~j(M6y zOjzWaarSKHT+{a(>zDuXsqg*4fv==gcBMxqH#CVP3~4FXm$ZCv#rYe`R!qW#)L76Q zzvQHZd$^`wT$BIO08Mlgt;9ry)O}J2vkZ}RUQfIehjT5Lr%zT2!DDQ(h8P!QHLyCj zkTdW(6b-5%j*yDk6*S<*hy!y-5?Ce-#7&jJLy7$HQd;)NRjdHJ&nUZ)X@E`+F;vC^ zUPZa)L@z@I)o|5B8C1X)WvF2%kW}fO(W8xeJ&TLQeQrK?E-l7pjV*JLYsM*KVjWI{ zYg*x@BsrN^Vx zaLxEY2^y+f6Q>Wch0pW;JabJCPOBlsTt6l?)`Qe*#BRgAFs8!?_N*A_4?z|gbUj6K zLC=(Le8#nPmj&mV{F;K)haNl}Ig(xRT6Nc}y$7Eq`SQ=NdH#`AXIo2~Gt%d8@?W%}X1ue=HRJ5r%(dEr55keCLZW2LZeS;^QnCdNsskM9e?ltd0*NTSJ z)-`8j97;~!l93@$^0uj_hqOYsDN2AQxy$^VnrE&FATYe~e7dv$xUdO)L}`6NR?>ZY ze809TG`^RJ^zYlZPb_Bc>f}gS%hcv-OT`)h*Rg7Nq7J36pWnqV&jttjvo!(ZBSISt}o2y=L>8J@@Wyf3CCW z#nQr;N{a3;jZ|j#_VlhDS=%?%zxCE_Yu1g<1BL!P%DBD{wKwf)&O29tu%@5Q9N9PG zrc*+ER9xizap0Qi+sih7tMg0GbUeJGx<4y2&N3^}CrY%PMXniV&#r}Q78MpWcMYsu zGZMw6M0?#6)radX7EUM1MjvfFng(JToryKt;nb93)l75~r{u`=-5^V8qMkgOyq>ag zg>s0xgyU2EUAP1O#M)vwhSfp!0O0Q85PJJHB?#$cS;WUNPMk8DE!30Eg*nc8gaV7e zKl$XcuYPrK=S~ZFhO0P_WbhkCSb121%IU0$p&Doaqd!rD%x?N3*Njuf#5$akYo;U@ zUiYbkH{S4cWyMC)#`QR*e2_@)8S-sLn+a>jj~_Rwjb9Vj!R^zVHo2<%q2RW@vF>^|0ew4Cg)}L6M05wWFJ^u(R$(=djcbp4f1DCiq~Q3ahQcG!M}*QyP5w*6G`TH7q7<=mGl8UV zYPt%=w1bZ#ok|W|GjvL$Hws~~U_kvcC7c(AU~+0=liCwlK@S6^I4qrIrd8NgKt7Or zCcAstvfQ(0E1!P4W%cT{>(*hV5HfJq(e8I+W1yS*=L|X`JJw4Ytxwny#GfCF=5~u* zGfo*3>u^f0X#_}KuFs@e6ZOPz zd}K8vV_#a@hK!82X)8?tF^u>0?dX{M&VIsta!pI4FRLx=YO*}|+x&a>AL*?UAv@p* z{0Y~rD@?v)Af>+K&09O`P3b3i-S*$QXqNvgdBM4+7SIK<0YorLVo#hnv3BFy;+-XR zf8Nyd&jW}4uiMUFI=^w-Mj!8vKEImdny`LCu4!ud%Iv(0g#|AZ7ran&mx>0@ z4edU(`@)3__uhMN&yn60PYn+49x5y?tl3>#|3-7ow;CG$vbkwvGu-FbWFdTKNI;-< zbz8v$#d*&Z<~~?3ux`Lxo6@93>pA~i6Zxyku2@&PvZb(b`eY$DGk2h}cc^9{J2J%( zo#><4NwpALZhdFrvVzRHn!Xl$yK(myxn`U_n=#kayf_e&B#;7pqT%3in?2DI;Uva} z>r7rT9a?;mS@D>LV{&0)nWhTyT;ia>kS~*K69KjJ(?9*wsHbdJahFY^N$WBzgrVe^ z;B<`jPy!`~KnbSdg>E4RVi1(ZjFLimRMm(~VH3<=IrgJwSIawGR#&%S%^HOWln1XQ zlv9E-J);f3gia!sZs%IR6TK|><3>|9%jAtV{j-Z)Gfo*3>u?%eb96x#9V0@<&P~cS zQ*XJYcInbC^6XobG{KlNT$5$J=bn3f5JeYQcesA z;iAf#O1fi-&HEpGVB_YEk<3Wlp@!;5>KgBC9$Y)b7KB&!_Pr+Fn}4pEnVz}2Xz+{O zmv=X9%bNB#2C?U0%qD)6an2;Cr5vo-eXix6WkoFuHOh05YsT5L8FNkKhHUeqL0yBd ztavl|;Jk4JV&Rg6i5bT!NfmuG>m1z#H9EH@0EG}a9_gvAxGAI_zFLWK`uh3L|NPGz zwaJ%>YZ|O6jBddrcX2u9X9E;CrDDUQ+mXmUiT_DgB~7aDigeO1EzFezb)Dkigz&sS z(9b1Of|mzt)QdLFTm~4We0JZ{xhi--19=5pKBDWU?kK;)l`yvX>9z7F?2wPW;M!8O z43}`6KrS98N()SfYZ3sxB|=83Cgqw-|KyJkedfBCA{i@vXVR!mLppFx-{O1dp@-}s znjA<1lmI1GsK_zPt>mcA#GYNi{rTjY4aKP^htp23&2Om8pKGca$w)8CP0!8FXltsg zEXjA6Y-V~#J1`2Z?Tw&c=+2xDwQ-jP=bBJh+}zSmWD;CL#zO}WjqG1vcczg)vid7^ z8@{ml%%wB?Zr@i`Q>DUDX@teDojg30-JMhVaCy~NYis_ZzUNE*YmTfvcTn7 z;WOauHyO;ZqXeIHP110oUbmo=0M}F6?Og7k8aP5YkJJJ<-84Y<*2t)uv%l!6D(T097iQWdbX*gum58&3o;JFP2Jjh~hi)`~xT*K0D0$#v zxL33#l_qm6Y(`Jb#m*wvj8n$MI-DlgBuuy?u_jLwi%#-&pBhO|zrVC}I5RVc_~I*O zpg;B0Q;$CSsJTHTn_Z^^aV1a{G6Ork3TUZ0N1R#;JfUARWKB!2T31zjUwz+8 z1N)xezwg+7Lok(37i_pD;#kmGn0-3e$jyi<8u`Mfz!;;QX%k!!};vl(&C zw4|KEq0PH;3$A=N!g7^8+djDe>q~3;3Dej%Pn?w0NOn?AQCfNiD#qST-t$y#f=Xy* zV_5(W^x)CNG7Z)gMh{F>(446IoRq{v(lexF;LC%)Z-KxH3T#z%heDna<*)fo{4Mg|&{M z(frX+kHUeHJBFg7FUW<8dh^hlCvW**qZwv*XOU~hDPv+CPKRrfq|k98og~%aL?Yr# zRK6_WbF5^JP&PGn?b>zb#EDlum%fqjV#7O5H?-&wu*7 zaZOFC#jyE#_+W;Yx2!bj)T)e~{Y6LmQ}=YG-O-=4xi#s*l{XF5CFNXM)lPk^APy$x zv;*KSiNG~2`)ZgbIfS-cxne_mcJJADdjFcs>jqz2x$DyI+fLuM>(*U>JGR#@GfAbTiQ0ppR78wOT_qrMX1KMo(pCrmSC|Qdwz#?zTm)nK(Ew zPEDrCHG@EtQPN4oClMECi;G7)c{$n5S(&#N75-^n?)RG;|8mo&r`p?}UUJiCla{W} z$ar&k@RXF?#zvv}#_79iCb{*jpPEmuNlO95tUtoI1(xKdZdqR4-CVJ%Df8H1&fUWq z12s$U9I5QA&B=_6zTE6c>W-m?*1EE+xpcA;m4)S+DnO($KU`C92jB{(i*8Ffu>ZiF z_uRGb^uC=(ckSN0+hTG69hyhmb2?lT;0ns??gux(#fukDpFS;sZb<0;_utRik2^96 zE&E$5o~bVTVpZ{@rC9^n<^Y+xp7m(1zx>CG@|G85WJgdppwFqv03&C|9n^d2qJIZ?JbO<(kD#u=3vaLtV5>wjzG`9IuzDW$wU{mK~M zlt}K475%AGee!Wm-|Uoyr)1^*Nz0DkU2{33U^F&Om#6IWgdjV^bJV{&b3VGF#IG_Fk{c#tKKEx?G(5qzMeBTC);hA1|Tu&o&HwoGLGC z8|}^}-ZpPsllIb2J;Ev5cD5R6v~yfnH`I`E-{6fqmzR|mmP0`*j1FdCM)ya zw})?LKAe-$6G_iXhuRF1Q4Q*r_*^pt-C~<7u4NqNNAlb9n;Y}%4Yg$3$iD|1n{AR5 z-XNAQ7}SdFvck-9W+zN?`K+8;`vd z+iL4Zy?2P$lw4Dw+{jBD5u5ylU-$(p88I=&XzG;03j0Fj_6^F!L``$?Ybv@hx;g0@ z)+8aPAP?No%ZdgA@kJ@fC3+`O2F6+j24!3)4s!!fWaE`onV@|xsoJ}@bj6BDZm!|` zh$fZP>$W2&5DWAX-IOjf5Tkvzj_&T}{rk)N`!llMTp3rH(oqFeHTZhnBln?)l4oO4 z_X*y`X!0W0j8n$MI-H7YVuYnW8dO+dF@*{Orynz=Mx~?_{K4-Zf8Tq){oa57x9@w; z_y5EDe)NIg{$I^af4*Yi+48c-b8Gcbp`!BZ_f?ts!Te&@`jG`q|CWQ{srfn8UY_N zSW`$SM1KG)e#s4q((z>)|85yMJH(zXiR}yoZZt{M4B&D$@E_o>CdnCYf7e}i;hHup zGa#DAYjUFs6c~l+i$Qg*b&;${MqlI|T=QH``mzY+U`vRHLR2r-y*jy6sKY$Z4GQ&P zFL^#R(L{YGZpgO0<_8;3-qx_UAUofq$xI6-f1J3ph`y`JE6Pi=O1775Y|m|t%P0y+~%J1!z*|crS}%;Qy+ciw!{#hmO14KIlu!5LMF1>cz;u1PJy2Q>yU^Y+MY3Xebx1libKe0p8M zaCbxQm9LOF)ly%+ts||XAoZ=l$eh&6jTY9giE9E}VQ!NyA$4#Dz()&vT@`V^?D6kL6~a&&_Cx7~ZU#$NgZkofS6|pKEF*k58m0Y8>fx+0};|Zr#zc zIX^3p!JEhjGf4>=KQR7=yt;j5J31D*W&+rjnRCra-gE+=3M>sWK#wV_$**aJcKs8g z%Sq|uK&YfBvqm#UGdM>T2nv`UOKI|J@_jnRwdvjBsYY$W0X%Q`p?hPBvNQX*Y0`IYR=SDj6&0gH6y{nL<62eM*4EP6x_w2>xr57x`re!gq#rC+o1L3y zEJWNTvwOJUT$5K4)LI)sh&&9X$6ZVh8eL*@WP~$P;M-YD6ov+njiNSyhU4RgsI-zt zgZSR5em{UV>)ftI&Mf*h<7~^nHm(_{UT1etWAoH*bzGB^k~`DN?O;}08MtZq zCX$3u@^OZdJXj;C=KC}~h=UX346aRMH#s{IL}&mC&`mwGKs1#GKn%zUGYI7&@lb+h z0r^-j#%L&@yc~lJD&ayY3c)qYii=mTT2+4T+|c>+ZG(gL4Gk=QC`x^TuJKR+v_>bs z_q|nbyivYl#Z5o_;o5I~t99*Ko=a$kA$STHr9QrLnO0VofAC<%H@>lYE^qC{Rb80UmY2FrnB?W%8K)uk#$RM-jJHwk(!zrZ2Sae&Xy3)u$(#NnjB*F#i^Uy zGkThe@DiFlO#(5)hZ%DjxPcnle{i7p-2JEe`+5T;G2v}(tp?bPxnhm%{g}|i(E6#p8ad% znk-~Wc~jb|+tZp>n?XOhqy?_|vp@T@+?mh@<9ye<-US1>AqCb&((y~47n}*O&>b^} zcsF@KwQ6u}nj{1^2|TWe?_rkq5j$EyiD-lrP!{Ev(qWhKp@+K3IW~YGj|&k{+GLbY zV{`M!x^+I>!s7tD6y;hsrLB}SN+OTG@y2aWJ~_C1cfpT-wDOBzEEyP3vdBJ7L=xPz zy`!Ug<;q)5oJjFC_D3FBy=IL>_~~|&x6+KyE^^H{WlXHY$+_m0w{F3sQ5Oz#XrhCi zAhlx!V(HSFPhWSepx|(J)~z4<;45#%&~eT0{pNr8{?a9{6c;^QQgXhu zhTLXp3M7KD9j~z^FI39ha?OnN;rgV@dvfpH-n6B!thr{sox9)zlnA@PCh=VH4%S*? zZEan9bxw0>N_OT?xL*_M5kH&~p#*MkuDxIjt8H zM==BN8n|huxe^=(x+(AymemwV|CL|)6-_Pd?$bfwh-(u-8J|N7SWkch%AgZ7&9kTr zN2h{S<*<|=7Ae+_n~rP2r8}#4ZAVlm>?^;uwbg~;xsXwMszA4U>WL@rK5=5;;KAPS ze}CuY%lsv>1-j{4HCeTGZT_iKH+=7VX-k)G-nbEpqS0#W6vdysp=Ne=k!!{&V`3dn z$u%Lk9uL7K%1j+z7?r#@f$*)M)Kn`Xl_n)sf8@jG-}j!sHdyn0@A(>ODo*JH7=SU~s7zUnuTo7V% z8*ptJWGS8=V(1b>zbFiKAh!Xy$2P9#;?#5&r1FU>Q#rht{FgSomS1ypHu?i+MmCL{ zIC(;zc%Ab3oq~+Ufu`2ZR>YLjse?TF1Gr{z=8e7Y*pvn2n#Gw#x7Qqaz5fLtI-iJZ zsvgs*w=*-Y>5br;446X5?byekhm=LG8E4P_)p5_2YW^zJQx{)zUZQ*upY$gK1C zzW2Q*`e2#xK>*#1AO22QO>=tAu*GT;JP=q7&w zK!7$Bfkt48h@c%xfz^e;i~O*QmI3^ULNSzu71l9UvR{`QM8q^hC*?=!sWRP5&I=bV zK(od@dpdvcgQHJB-Mf0VThK?nM;&8%RtyhkKlWJ3*T0@}{q;K45DYMSOmz#joptqG zU;wx;-EG?LU3rd;aeI@BP91-t&(i|D(Sf z9{SSC;U_CA9?H+#9*J1o!wUHN)RV6cK*-KDc1~-lbxBqt|{sA&h*2>Mfth& zJsIDt)c8Wi!n;T(jMMw9tg8C|*?SM@IL<52_nnzJ_IUC;$2(*B&Azwuc5T0%@yw1r z$`muY(K+XcPH3P3G;+>4gCM~GFcZu#>_WKoz zYBd``6C?ovpizehtg8O%tE#W={ont+_X|00d7fXZRZrxMM@YbAbIn-X5w;Daf`E`h zvPd)nXiHB`{ClV;E*A0+`0|%BFeJVmToa8D<7x?#NdfShNE;XpxKkU!OZscVFKKEb z2i;xWoAzv4bh0;VYj)4hMK<@yE8l?H1Qoaz>=t^^s#E|y^~9bb;l9HUCy{HK6{cm5 zo)eLMx%TYg4L0I+|&qiBBaD6Yx1v;*yr0Mk&3WPP+-j`#T)u9;xZ{?NE4dGq;t zhqSogU)(5_+$M((vxG{rMkREUS>5fV46JaKJDH2dlGWih&I3(~% z^Rfe*6j>mCG8@>19sxKwrDDhspkN=of@M-OcLBaAhQ5gKHJTofBA}xV8<&HMz!&Jp zU+NL*CVFSj%w*CdMmmzN$;`^?diB-X#fwWTD!@#9Y)~&dKOd^l*xamaXlUHN9p@Yb zqaLp1zB~Z6@jPZ$Fw*N;_yJ2W+VkAe`5CU6po~-Na4cLC(Z?7NQ$Yj~8mw$`2|y_I z`m9-xZ~cG&+w07)`QQHUUm6W>6&9SWt2>+#UanH5C5$?!a+D{z4kW+h1pPXpTvMx* zW;zwcna;*s+o4AJ>Po%Epc)^Y8*_7@{D2ER2#ITgSs)bNCqAoAr)PqFyHRaYOT|?y z$6Vzd6y)xc8`tDb!Cz<`sdKQ8cW0D0{F3gKL?7Xr@Ip3lla>K+#T*c>7cWWF0VwCp z@E1myfqr;Abz%Up3RJ>uouD)I6D}n$-@2zQbUDNRqC2=PUE|Px;(6Ut85D|*=YjsB zde{swnlA;Qi@1MF>5&d5k!vz|K5PoM1nc$6WD!Ri5MD0QP2A3rUSi~P7%lh~$VDge>_8|82H?l;4w^aB(zMOZ^Or1{;hG6bbblU>g=-=oNGf8D zF-D$}bhcn9`hxj+2EEWtSS|}lQnnfm-}9yYq^aq}+}uqC`F**$ZAwLbN($3Md>X^U zc%vA%4VT8m^fTL;IIfA#d5tN9mC9{h>6Lkb%8)YGt5T~bHjX&F-B5nefnXk)SvU}% z7~4POD(C}#Uwt^Wz_m~_VZAoF`{Z&>fE9=YRRK-HV?Z`L9FiM?9kNeu9jb@e1GR`W zaZu(1LA>K5zkJZNSg=>Oc zU=amG-kIbH`H9+i9sm$*Btef|;BsmcX{#nTcg`<fWQzWS=$uMdH z-xZjE{jj(#j1yV|XeB&Ko1XA@7t zFKH`i{he}6s^t<4EpBRzdF$mmhsv&zPsqrF5#M5lYbMyUKR~XDt3!4OF(y2p++Tu3 zGJDaV{n?+v8ksDU9nK)h1T|4@v=tAE&?aP*NGFj$qL_p^K@!|Hh6jlOQOeLc2aC+@ z;BaEEO*~}aC;=9@3hkg~;ER&*PS__;E5<%YlxHdj5;5^GPOha|@Q}9y^r)QsdLoh3 z_up5aIkRQ&UJ#P;j?X;v4CU1-6$lB0()}oM>5}S$4`!b~Z`iWM^WJ-!Yu57wA47@#g@+AF;>Dytq+vd;DoD3P4vQOiH+E^4|msaB80d5dK{tJmGA&9gpKLO1EG<7n* zhdNn7m_}w3rtREO=Q-muJ!MrlYGt})T$6f;UQh!%h$UlbH~>AtsKh@*{QjhKO&TpZ z*JRj`#cV0gD-Qd^@lSr+eLex-OXQj&ZctTJ8>ur?*cH~%MkC#}+B+>U!!;A^*&im? z#Gi!NLH>Yq{&wY;QCX&Ov?vPGmmaKjbPXcwhmH;PtcIYOw58Yv(96CtF zA>)MLrszH!GS5kH35$#)N}nJ6QH>JL}}Qy>zK8C;Nu6oZsW9ul?UNq8gP1&a^* zvCp+sg(Gb>o2^^6q^(}PX5&WcWEr!;S6)#yHHqt?QzkbWZRzgLWK__aHH>>NxPD## zlb`sbQR)%(G%0rkRd1bLa zyc1C+MiC2XN{U$`5p)KBh`9hs`N3o8sm7=XtR@Q)xme;tr(~{mEh?Mv{JDi7gxvlzahg3T;p9Scd+_)xSjS~-b<1em1 z@`=ww5RvgWfGj5&7=##$M3eI%HNuaqihj{>u1V_=bmTlJPgMl=ct>{Nm&__9j@)FQ z`u9J%zweo){zE}kraG=W$t|CU3*o7RH(*qB@f0I}Ov%J|!PwkVbd-b1<(f1X&yN8Z z<>`del2mEf9Nd(%W=Xi)qP2{jBl02mB77R|BG%8frN)9LW6coP6wgig-x;o%V9)*l zxhA5Ban$Mbu27CGT+ED{qlq19aYB`3#-Wn@Wn*4U8V z-~avJLqOT5S|Xrg=oHlkukhsn9B3S-ndA(}6N~YS&j5Oeh{O)%p^Q+;5>XPn$doZebg;6RSZd%xgcyUa)v|i*r>QQ=V>yyUOo!j%GdMFHxh4tk zvFE(`9BE$f;=1gk%cC_#LT7UVax^Tk!dEUUFVFKRSLDvM>Be5=9eftAE+#4tf%9OIvJ{b4KPEZx(lJF{z%02_RpugeA$>o}~1C78ljP`h(m!wL{jYU_&rw>JTWZJ@G=b9K!J`hb5quqSauq!>o64J=V zEmD%?$;F>$xMqSq`@`g#430_F8s}tnJzB6OW%Mo_jP;>?;Esq5{F24Z8ITUGgj`~X zneYhug}%Tn$)S@p#~?y632lOtFj^D}DhbH2@e|CF@&Fg!GB71N#?U*AX-w(F0+itr zt_80s0M3h5jVRu*NidM|P)s)9i@XXf6L=Jmkm)@2`s%tm9Oi<&yvjp|=6?LK^UXH{ zTep&Eph7_n!(s9;j0ehyMt2=JuzCM}MO#~WMFl^_?#L4Yzxea!8TeR(@TIaMnQJTcPd3iZ zate!XgJ}E;i^4r6xt!xOv}8z3pE?DxOUGe{Z#}tO6Us85qO@=tTGV zi$)Oar(+4;7sZkYJo-Z1j!*{-7*Iz0s1RmMzAPbGHrmHPttM+OcOD6I2v&%VFxnGx zK2wvv#k;ycu-Id8C*hjPR7F&uRc$M`YpgI-r`uID)LApW0r`9wFfs6xFO7OS_U%iJg#oom7=#nmR6Uz3J{O%l|EYr;YK z%TI(Y0bc&XJE53l%%O=mmXrilu#ZGW@v^`fAPD#Z2S6dXBO7>yb*3*6N{1G36=r~6 zMJ$44if5=FU|}0gF-hEGi(#B#4hn_4h*QVixt+jM>*}0?gC!d`_OD*u*w$A3`s+`8 z{IT?%cbqqFcs6fV4)rPGVHj1JotX&%-F*K1#;aFTuU*S8E{<1Ko{icPeD%Bi2sf{r z;hG7`IJFLwaLpkXMS?UV*~kPm5%R$}5+o^JXokxNa*5n`bhKj>G23uWOe~fXH9${@ zWjypy-9P`+#Rc;xqVA$VH~@`?bgqd=mqM*!Jw%y`*mmeJ;)$H8{stefG>hcpayVXcoLV3GXyZE z1=j>IXrdwaGRa$}vI0DFaOS7ws^qF9<xQ*a*Mn8X@+Yi*t2QHHKi%?)UW6FJYsU+#iti9LD+N-ueRgJKNKEXppwrv zhGWZ=46ifBHN{3_Y!E|#z(5!z8FL6I-8UhWkU}PGC;Z8Zlt466jadYlK>+9!E*yBp zKE(hTOe$d@00;9*e1|bT{KQFA51NSWrWhMLkWL{8F%(b)+wp-%w6?qv^}{u}FIVvw z+f7N%14P!2j?laB7Crmy#vMD_mMwF=|GxG=|5N(jd-Fd2IB@Nnzq*>hCikVMyH2CA zUAeOD>eZc>E)_lXR90CTWFpokJQ7gG!^FhjtOdyYvMA5IdEQyG>QYkt@iQNx9>nUo z%e~9oc-1iNxTZswva(FmS7xy@G3HP!=v9&mzofvY(2X=PI|zd3kl`^dYsgG<7)hPL z4ETbe6&qL}#L|Q%ttVvTMw7)gH<4|a94wQBHQnAp zWV^%e>UQf-nwSeS{o9#kPgXQ;YAUW3;-D1f610P<1TBQ#6&-N{Pd2!p(j)SKU!8K8(BKdq64o96@}h73v#0p31p^Q>*qrX>!EZ!XNK>9jO-%-t#4o@2#V`KR zAN^6R(O3!yx=F^IIo=s0gg9Yzi0p8NQRi??Ruw|=A$brf)Px=WLM!o#5q0b`92CZe zq5&7=VE_k6B&tUx05~Tp56*)jK$aNG6w3o9P}LFH9+h)bD(4as^u#q|#hAC#{K_lE zXU+^9J=(crhy9gT3?F@zf8&PgSHCh`xWJM@)X4*ow4otn6%~ihp1pAK;!1`S9XT?; zyc{MfHZ_k#&EuPbnBkf+xtfYQW8s-Og6?w~94CsIq=;uf{!7Ww&kCesv3qfa}osgz5z1LkPDQ>VR| z_IBt~wp6C9snj`4s!!BhXm_Pt7&xfa>gbLGrr;2J)6xJY5xIaE%r=3yfG;h}A=6LH z9P^XLH5I9D}5T>6dwb>3p$pP5-QS}cd*M=ocPN=XmmPp96D)jTOX(~fJ>N&ps6 zg)b{YgH9=_&o)+PTaCJ#gb~1S>QfrSl8T_bGOW$=+x@;Y7z~LI?7av^)WF6(y;*+S zs{FYgL&|ujIzIW9JmKWTHMObACbjij&a@u}i@#UabgHFbX;Cy+XaNuwuE|D(07?hd z1F5|YX}Y~e%QZ*jVou@qB2S$s7!3m4`~+SL!3LMr&}%fDHmB|NyPG_bLg6anZ3?f_ zyx!`4(eJt8bG+hlzT$Pi<}0{fwCkze`}ggqdAatJ#u(v$+yJ;Qwt7G8;QZrc=4t-YUXlSCgD#ZyEWAH zoHCNMwAnxVnfkfsLRYSAe*XFWM~+-LcW%jw6;+&Bz1lj|E0kxWuN^G|58l6j|EW`_ zRtyYOZ`-D8ZCzMUz;JcWV{u}S5r6A@*`MK>3CcLN4#&T%@d=|4!F0!Jc-E3ZLGL;t;lpyPf@<<{jef+US zvmQGlmE^|842@LmvpY8(xu$4z0T)zVY$=yRV3UB9cu3plI2P`%oU^xf;oh2sl|gwp zmeNf`Gp?zqR~etOK>qSxDy%=;P_U#hzc8P-27H;n zoggBED9J{VOJcnsZ+4o!%bE9dVcC(2qD4iv3|k;Q04oKiX=hQGZAJFNy+z^W85*x9 znjeK7N@Y@Ylh$t<(fnj2=XaLtqeod^u=I1~EdGh156^xUaj|gG106G#*T$4yAiFMc*S`QS1+k`@)oA{iB zHhExpBsp}EZsH7!sTG`0(X|6H@Js0k1bT=ePw|nc2W6yAF2rjE8N}B|Yj9{|R8lMq zX$AnP5SN=B=q5_Xt@%~Tc=okx-6u}yuV3H#$}6YOoH=~>a7SBP-@0|EI`!j(TN$Xd zW(_MHa|!crT)1$7lzL-FhkVD5Ia{_c=$P|Vds}LryfKIWI>R*+lyPbuj-6|=c+f)+ zZur*3Z0IGdk{4y)Q|B-`f~ zEG(fWk`=;@L^N^bX#vjA2%vd(QBKS1mh9S`P$cAXyMR>07>uT=5oTV5!zKMovJ117 zY9$O+;Gp_c#bUMUkWN?}XrEfKSIuAL0gd;Vf93v_%*;T)1)5z%@DZh&yYo@_e9$$r z0YncfR%{)+RERM;n;b1b*cNg4#Sp?xp} zB*ew~V_``6uk+jAp8e}zH~i>F$1h#lzGn}^E17eK3sFhZ_Kv|p62p^7Pau%#^#-(BAvOlrL@SVc7$?4w&1QAGn{?X6jMHV^oOme__ZKd-E?mG| zo>OIb&@|n6j*Pia4&?Myg;q94{C0IH6d=F`co~ht{MY~i zf0>#GXB}XQLCo;)b&qFwp2^{w2%kl+Yt{$$hiVVjm9&-mGJVBG#h^0{MM!|jEb)#Q zAq0-I;eun3kYPsrfYG!q@iSURWapR%tO5ZBrjq51NQ2N9quXRyVN&kX2t+gPU)mx6 zirxKt_v{6oAxue{H^w#j9H4elVUeaPRoiwVKF^S|f;F`&MWocv@v%;iT>be}w z7bC|GW^Lae+1BQ6FsVoAMMFEoCWRcj6&P_lU7s#Zi5zkxc>i$YdXFM{+skLTW`aGN zMqCrsgQB6}xBbea7m&^~xF3!#*XOhb{JuNon(lcU;JX#3ok6)yqN`fK|j&rDCzUFYnONL zUYIiYMz43`nyi$@^F(rTSh$w=;ygH+@L>0TIKwsLC2Gp;kA-UzQ$aY;$^87H(vn3^ zXQ|(x&LKjRARoxFXo-P-1R5D7QHZn=C8FmCs`BU~y^lY3PAaXHeTMTbCp_fX52q>D zluA>{?^zRYp~IO-|Q}uAFF&-DZbS;tnCPq>XrSrt5+W(r!R4 zt$|IVl{iEfh#Vl>$UpcCOcQUT4B`!*hSP=V=xA#Ed%MeFT4d24HfT>6G{^MnqdN65 zz2>;Vw%56M>*Dq6*TZEg9G~Y(Xt#K-36AIHxMqS$n_gTK)y7f0d#R909=AsRYsJ~wnM(qJ zJIJ9YG@5uw>U`F$Sr8_InmFNDACLt0N%u{X?2J-J^6{nsAE+eRLLMH702xp;d{GKx zcEKo;hZG<`gJZ_!!`}{7LSML+3aO0?0V2+@559>?#D#z!mBUKKE}JoPPxjq$li=R)2Oi^@ z8Ll~^TuqgeNx3E%E-v%q$EKo_m|=t%c|x*rq>xk!#LEGaiZ6wy#b4weZ>hYzy0f!K z95;ulm@}s+Rk~WCAWJ@`g-_xu|Lpgt5!X~Hq(xpyby#IH2o0O(*DWs67x>f~l`t%q zZ-M6XUD!9OBx?$0pWED2UX<6<-OZOp^F0pJs{A?oDrYs7q%(4us2kwDj!y(9 zv^Kv2-xNUC_!itfZG&I4S4Wgo-aZYurb4Djw*At~k})G-m6Gl$ISoKL5ABCA2Ad`|U-iPAy-#l42+z zmk=L?ClFu)vLLTK>a}avHf`EO0T7IKhAzVg?zX{`T{puuV}djlcSh%$vRRL3NF|HY z(`%R)7U4sN5Fq3m0p$<@MOfo4C9;U1BVOzXNiL_&Xkeu6{+gOCk%%y84(Ew=DFcB5 zlgSYi++?yd*}qLAu4zdpP_6{W@9U0vuuD3k8eZ7rXFw0>@Bjypd;kN!6}74WJF{Y+Foxm*(-38w`3kX!!J zL_`9hcOW(0j2P1fiR26flmZk+wrN8e)9G+VB3Xq!g@L7^^qO!u6!y0VGOuJuei$u# zuCi=Xc|lzPuuJDoU#H)9Fc3Nu_B`!#eB0yvt~YmI{^os~ara4eVd_Q;tI#++2Gokg z7K%f#nSnv!&J5#WtM*BwvQC{kHTrA9=SCC~ISI#xuxhQ2beC586P6em(~?&J(#HqRC z|H53Gj8CU4C&Px|ojf6y=;4PSCb|h401NoTgaa6FPRAYq3cY|7q6L6CFa@N*P{A=U zoMNa3Bofq0cs9yVp7U(Ln&?+k6(>a#k1x$75J&hTcZtuiH>_GU@52ut|NZahy!mGD zsZ(8jeeh9SK6H==V3{=VlY|eqC@v|v`mJy6+O>=B@32gslUqP-@449%-bOf_;hLYl zzTd$eN8_3d(R=8@+Q%P#+G1X=kP8FKu)FwDNS-Kj2q>l(#>r2y&yGihv>|i+6bSfa zsr_^3JY8C{KOD|NUa0~3!=h6rcF9!UnMPa_1{t)ZMAI}*t3jhtM;y}S#mcHoeR@EM zz@hU%K+t11sAa4H!~$m>?QL{#An}3P^IdEvO?|FmXZ8H01=3Y@-sU=C^0v_;Oyl** zfSbwXnq-l{YrG&B3}8xAfaAy~2+qRgWT_|s(c}z<1ldN~9d-wid~=00d?1qZQXVS_ zRbQ-Q7`tVIoiXO-XYKh<6}292FX|}vrTYkNdIDZgkvG~A&0dsKy}!2PbXll5ee2Gx zG(Hrc8}VsIY)={oivnRq-S~oHu%V!$KvSfxK2vMRGzuT`b{pnrqI2jDm95*=5;XdF zV7Ay3snn`sSHb1Tsk7OKOFV@M8EuooxYW^oByU$qc2RM(Xi;-ddz-M#7@`Pz#43s3 zq3!t<+Ko}H&aZ6CZdx5!+U%?~sSJ9<&9$M$@=k-j(2;A?++5NrzLIg@pNVTG7@O(B zHBk*zZ@fqkTn*v8E!TuEVsPW>dKfLlPk;Njf6K^pj3y(57>!QbGByZ) z%wq#e02CY1AG}u3iMS6m5z7miBTT>Y0 z@A-v=m;fT001-Fk9ceT)2sLl(?zUdKH2XKdQC_*ywqZk4OABR&06<8DvP>GApvd&1 zAwl%!o2<$U*Q7Qvfj&VQvl*_LAWl>3a5Sz-uwdRi_w3n~QfVfm!pWN>bBHzGQjA>U zrU3oe0MW+a3?V~mwTkm@x6u38Y|67(S{fQwWM)PXHB6@1xed{Ws*XGEk9&pFh->oh zDN^B|Xzex1RIgcXP)X>B$p->xs>=$Nmzlfrv`p&GIBNV|ra&W2%x48(nESoFAboR< zZfnUCTg&EETwyK5(tuGoCEOZ1jw=uTg56Bj0gh7u3HB^t{ryPf7txaIWu*tooqIg0Lt6C-efVHz#gE)yd@n{nd#@#tXJyx@I$I+b*=Lo(26KddaqPZqL zCi4x^=km^V=e5%{)1o$4Tg#aFG9xUc_S^3|9S#e>{~;nPZDIL~7Cl zA^1Hk6M@8!rJdu8#qT4=g1;0D(g;FmCq25=zjlLfz^!-L>~?;QK?)NE&`DIS8AFHG z_#52S{tVYluxHbSYhuMj26k7UJ@KM3a!s-4CEYQZ>WJaz_<;;Xk2N41^vhrVGIMG& zk0#MgMx(RAHO1+W7$n5T$RYe*Y%8V~u)?ac5d$KWBl^j}8ju3!*sNBHH8wP0wZSyX z^N_qUKgA-Hs0_7pKAYpgu(PFdNnMOvVxrZLv_bilStYCU^&Cy`~Ni~OY# zAUXmgtf(*=jOAtJ>o=?ymt%#Cga#z__mvg+0(usj9krbKH5@r zvc!79VR^=$dn~_vOQpNOBUj65d1M|xjrs{8(`ku&0rXIOf(-;7un_1@Orhf2lanIX zq&yFRJ>XYBA9~7!b{LPb;PpgvO&Fw4?Oo;V%k$+z?X~rs@jaa3nh92I+Hg&UJT%m;Xk6rBj9ins+z}WEC9TAy?vT!}eB~<- zJn#VFOo%3vy2C;L=5PLnGL)nk3#Kz5ok8ei(jig=1>u@JFzA6g;9p^p!4Px@^9rXF z=_coS9+KI<>gw`_27G4Di(rn+3LIh0(H8&>d;nNb793it<&bJAfERB0?srRf?aCh- zevU$kKKaeQtrCfI-#+?lrhV{1%h9822L}mTVB*D=q!M!IqGe&md24g?npa*qbnsv} zBO}nzAU|>>(%ntSNi00Re`mO6f-+96!=zmErX(PyNGFyU$;R5nn?~>*sl^iObz01T z!{L-j^bb9>Vg9_6;q*?{J)u1IJ3XC&6s=62fIO$9WIg`aR*9sC+4nx7v*Rmr<5t`K z!8OI^kV#XTi~YOWTyF$k6_AkxpX{*c02-am=x7DcfeQFg8 zSx)4JAWb^gqy^wQa6!m7B&M{v)Yj%GJXXBq;?hMM7L`?(0qaOU5)S7hf$q+8EBe%o zE7zSivM8nHsy%Wpr}I!(+mZI%%lTQaM5~^xsqU`{WCchf<5>a0KshzT>`5ev9EhMP z*bT2@tSI~yN;;S-e@1FUCs7c4!R| zg7^!4CxOV?q0!8!&z0uUxSAskAxA)r>0`#Y9qkjlc0zY%xMqSqn-*MCB3J%fcE^L& zyXDH83pU=Pkig+kZMaVIdSH;SNoH|}N5V3hxmvHgY?BJm8TYnPLsnloa)W15f<+x8{#N zT5|5()}1@Sv71~|DuuQ#?(3^xwTh*6gAEN?=g-%__15weC$xIKd&`#DKmDn)zMeFE z@`k#*UuU>xf-+96!`pIAP#&QK+(B?88@c36yeO!IoOxNwg6%r(dc8iKfhdnY()7eE z=76Xo0D)nE)&zrPD&-Edagb>`XU!`3+E<^NH*Ysdj2W&O-;xu$pOPXe$nx#1*Q_qr zI?XDugK2m8G=zvuCUZkcxUZwStD}_-=enFrlkbNi`{BAhbBRn@9gM0)?GGOUe;5do)Z=$ZI}mcK{lC)-R|AHAywEUIP2!kn-?uw!~-x; z6c24g*2wL|xF$A$9g%BNk_-3j*+ZQo?kE2!p8?m5e*mU2N>jW2I|MPsup%oEK>~RrGH!eb82^a0o2$eKF)>}f6jqZ!3tuENl-U@l9dImO zUjVUUnZR-vRLW^ALy$eWWWhi>vKw2~Y;qt20J{=!XRoNNR>@JrDq$RFB-;^4uu zLpYOtu!VKb&<=j37?T)TpL{as#TQHa`r0}=h&#ef#oUPaJfH`Vf***cPzf-F>nj2g z=ZO%dc|52XL>5L%=#ih=^;uYP6w@WY(Tm#b=O3{_R$ix(HtLwyJe;#+21_Gh?ef-+96!`pIA zi_t`oF}fR)s@VaAW1PTSYQeUGYH*_Tr)krc+Tt{ z7Sm2eF8J)VH;+m{8T^&k2zSz6C%%NAO;slfC z06xGe99ce7-AQnV-CBSGS|3KeV{YSLSsI!xxy{ zf(9W8NJ9;&WbD#UcX)AL8On}*#X3Sa!x`b4j7p~G+~!!@XYTYUZ_aQ8Kv5D-|LMgO zxi`Z#6YSZv;F?mo;_*QFtb$eJ0&x<#;xTX0Kb7yCYYHTnHKr~ia>=-#&wcK5Bpk$z zPMJ(~#3V;#&)G<+vk~=#m_qwVpz{+r$qr}00jMM@5FT352T%ec0COTiVx$u-6DT8t zaw;kUEiGUWP=hZ=>Hu?0`I~w0Pac#CVVQsxfk|SVBHe^#qOdr$TmqVra6nHZhYl%M zt|aLW*QCdJ?xs!4u3qhb;RQmfE$!{@`g)ngvT$%v_M6{ie*gQkfAuR_ZZ5N*%>DiE zQ-1wx{mU=c)Yj^(){?DT=d4^Q9Yap$lLx$m|7W;nf-+96!`pIAup5~}_%L_?eS9uV zlmZ;2haXz=-~ZvQCuUt%tKgb~;lwsTU$D)HERq_|pyP1ZF&;?i;RYQcKGN@{J|)&MfuyRQmO5Ii{TncnMU)H32F5=P9pCvFXD zV9UybEBgogE2}C|1OPoNnq_D)8K1P6uh^~Ma@w!CeTRdUTdP(a?BBR|V`f$+tQ47t z2m<0DbwOF7uh(CBrMhcZx5Fg_N8y^ZJW7F1F)S%BH;>`!!<&?nP+{)KPZSopCSb~R zRM1xLGBvs8@E73>1`GkCS)igN`uu}|ZOgJ3r3catMguKC?^RZZ$0G7YM!cMpv<9zC zT$6Mj#bBjE(ASsO?rv-gG)A4-29?mCm159P@Jej1(P|sV^D|sC!JbVEu8BbE3?^TM zq0=LDU%6B9Sg0|4EvNs#G{3$utwIpO(c>4)mkEz#CZjKX=}WQE=ycOWiufBpPDpG< zTvmu3S7C{Wda@zd=n;`f&;wn7BVuRK9T9c_3Ye(K7d@<_>+w*YlAt}SM3O*&m~sg< zz*cc_DT&H(3yPtk@K)5-uY+#`Ex#wIz z|9NQp_SP+17QFI`b>&LS;lrknK59OC)OO=WKKY`YocZs+|JZMT8+!U_khZk0F6;T{ zXI;Mz2;ONUkEQ$!*Gy2xsdbo~Ya)mUC88?wRHW-mv3ngIm{Zq{)B!7>Yqfe zi8oJjT#W3tceEF+ELQGR6RNaau{*ANeBTWfUn=cB*wcTwzk5x0MkIrH0Ez(l^W=M7 z+q{k!Jnq+hzU!f!qj~i^8wU2R99TEt5BSj+WFEcX?KsTt&+(q~r`-sqZ3<`{LLi@? zXnCMKk{!uh9?3gYU~hKF^@&|$xtuQo6~i77&jaqcmS-TMK!45Y#Wi^sv&9@p52h+) z0iCbY-CUPZP4tnbz=LIalp{xuU=9-^F7dJlV!$A24cbKXeldg&t3Be)EGsNyUImeP zV>R)I!It=ICv<;?YbMyU>B2R|(BQbphD4!y$XD~v6~_(fZYRVS`V-%Zd!L@m?- z3_;qVkDNgru&t0q2&iaSK_O_RXs^)-Fo)14(F96z2qmQeHLydOoAlJ z&NZ=c*jF(o0TF>dexjV150-(}Bb3A#fS$z)`nFCw8DO@l(d-Qj3f ztF%Zqc7g&#J-HBmLoJ_F_!<8%oijW8pZ@W1a=-Te;F@I5YyEQ%cDkE#3uM|}5s>*B2YwT(q zSUEreNDo91m4Wy8v;1kB{QBc21`?^3Xk=D_P(rX6-{T2+y#2nkrvri4!miUEg~3knyQ3u9M*%cNj3sdi4T6?YIn(5Ujc6IxA(ELHZEaOlRRx=yGhTeLZ`(F= zMa8`9*Pr;!Z)C5&s`|-K=KSt=^WS?f^dJ9W|M|}|UVE*!rlxdYpm6{GCB3~&F5lYI zGxyAyg+KX8U1uk%TGrY52>ms4bLoG5r!72|@-tjBK^dpkVG^zhUL%MIC4vi@1Mci& zK~qzubLNC(sRM3zo6ROnYlk~Ucnk>^A)a~QXFpNDVE(dDs5&)u`K-q;TP*`xt(gJ@ zlPJSRRk&Y6wT(rJ#=i`s`fS$PKLoBRmr2{gbIvXgRp+`Z%F9Tm04P8Yt4gd}yLM?` zA8szB1g;7DEGjNpT53MiGT&*GFk9z@ZivAoa!tS#;sV7%bQwokw!F-;*QP&bVkJrI zbI#zm!i+{Q+gY*v?267coo2fktOU@-wjy>fx5~3Rb2_(lmA90qrKi=^*Aa1q=s=hR zyf&NJ$;!`iulKTix$=-!(V~)>1Wyb=7e*u1c=fxC#`6}_C9CPQMG;6GRY2n;a81rY zG3#pUHt*aV$&1JpiJ{QpA8YDxO@my&#NK%+`$TI-BP;*lcH+3A{s0T@#=t2Ak{2BQ z3gQ{DOBp6s>`nmS07tYl8$sU~*Q9N!mcpaeGw$bSxMqSqn-*LXy+Hl3N8=?AdSN_` zSED!1)_Wf{MWnKkyN8KflNN(+GQTEsXTmuD{LlY9z6}X{a`KB`{2~POAO7JV;F<(A zzw(u@AZc{;gq!lr@I|nT2qlgd0RnJ|zjTE{@&PC!o$yE$3N6ESi|@z|D#Kc6Xb@(d zKo5VRn8Y>Nr#1M?RnhwT!2bP4k_=ic79p>&F!%g<^@R(bjT=?(y|?_}!R!kc6d!%$ zefj0Q)vHs;hX3kUS+BiT_3dxl-hDUq2R|^jwJC4haR1;3Esc#lOl3oZk;5PUQ1m2o z3kw&9!xNN1=QU@zX1pLxx&6_&Ca^zePL3pHtI5>NT$o59k`1yC*#qgLk5)eV$VHWM ztx9FVXTr528yiD>rak)SuMa-*(3xFtP*X%cGFWgd9Yj2g6yaZMTxHc6J8Xysr1)nCQh5PnGmvOwjZ{K=nilH587NqeN(W>N_#WFV5t0M_N5zg-IEpipguc zKe#3^o?%(AsWrI1ueqkWiZO{q@?ck84W;{=HJw=!<51@!0I0LGlN4=VZ}*z!Osj!W z!xOs!f0M{Hp)cgn0cbW_gEq1V}!YlL&@)g66ywj0ZC&c5$xksSF>^bYAeN^KQ<%0J!Hz^Q|2=#a0y| zPx)T8e!IzAATT6MplBFqcXr^KSP|wt&#cYV`E?SR1X`TEB1gMduiUCmC17#8FEm+P zlR4fa+Vr()%d;$*lA+-junCjil#v!r^9rLGutRW7sYIG*%sy9o>{9)cmEKaF@@5ka z7dzrEC844X6{~AAs+dp-%>{F4AD=a?&(c|IDA&l;v=}f&vnT2IIlSh3JTt>J6Rg@; zxu#S)U+?_af`NJNf;rXOfe$u-3sI$bsia56g*@B*g0{{Oq$l z8yYHCtF8O?`TqUia~m31(E)awtaa}Bw;8UPpo~-Na5S#Tcb`AsGj~q8RDyl=6462O z!0|YFAWa}-3J_4zSfcoT@+&f|NmYjk(s?=%(4yP_?VkP#;Rssf14; zX4OHW_KuF~@?xECRKuUV(Z>EOUtzMirYHco{v~B4d2RU>hbk)%REHYU-L;ZfBq<;P?4?9&7+qiAvbuQh6PD zu3cXL#gJ{6BULNRR2AqAYWC`stKpirx6PBqH5D>Ng`xPVoDDzG%zNE!{6 z3D?B7nNkg_oPDonee1dCxfS7FpXp{NoCFb~DO@(^%Iu|Qat<#G^m@#0nwQ885}O&y zXb(5%+oFn8q0y*Tr2Et$wBW>J%M8~{uxDfEn%@joJRx7w$ zTfm2wri?vihKj+@(9;A-yX!-^CM55+my2AJ;3XsyPASfO#2_K02)fBek4!AG*rcET z{O2jd^5{&uNykm-CbMh;m<$-g5TgQ+IdaobOmqRs=OjA-2+<#ri4x$%$O2jD8YY-1 zCJr{B1YTjXF}~n~s3~9?uOQe&pO2h6%UO!o*7WvzzxTamFTd=nu5N8_r@M>QY-Spb z*8cvy*Irw8$M*?oN}3laAR;!t5IRGuEK%eC~{3}?)iD~_~NfvnBh-deBK zEp(+9yFl^q&?aUeBD4vuq%mn;enp2^FQIUzjVHC}x9P7`o=m_qld?Oy{gDP-Hx7Q5sd99i84RuXm46 z+n|#vr!Lndu4z+Pi!J%l^c?IQZG`|}&v-$?=VA^W1++*_>m18oO22d_d~iiZZ^RZJ zO-`7JbA9%}+2Em-zP^w>n3s=L&7*CFxQ;iZSJ68`Xbo_NcH?%eX4GzJAhs%=AFZ>9?36%+4G(6^u7A(?2kXz z{O5mmoj5^9B0y|l`*!b#AKF%|0LP>X#e&P1Q-Ar(iru?SH*U-;EKJfk_x{ri*Gy2x zsdbo?YYJZ<`GZ74M27fcVhkM1iijhBgzGW1apEF*?6HD}9z2qg((7`CxmK%Xi7mIs z6Vhsh)%Fl^!iro%BtPz|iQVO=Sn&g64u3ELE($kx>Al)j7jX$Bq@>~lmdms+RJ z$>f?gdqsCe?&$*W8-dV!nVCPyDZN}#y`?5t7K9zbW^}oF^C5fIYq>QSYIB$88G|O~ zgM=*s+=wZR6J#fr32_IP2`~@|5JUK|sL)_I?`mP$>T0_71{QPNiWRL1tL)o29>{}ze@ce6>Uhg#;tR$u`SJ0XU}lW z&)D9Kooh-Y(wyQ-xxwlvYgamg$j4oxzhvQ8t2h5&y>Cj~LSNpoY#6oi<(kMjvUgjq z$?QcS9{n$IQQ{{#f^Kq#_$32|#P3BK9ZE`{PKv=Y>DdVrB^pX(lz1dEA7+_WyB(;3 zYoZHu2ZA7iPW;8Y13hqn#bH1gXgDQG!CUj306ysv0t);k+=ITvOfUtg5RS+t>A_$f zBldp$W7DZqU3>P_H8kK(A3S&v#>uj0HEnGjFTAk(si!txxzhQAALPFFTEn_^B<~e6 z*}C1kqwl}(IDMMYM%0|YcyZf>3%w^#NPZMyo4w&FcuyEz1q9=pWBr=N3XgWjf1mxX3S|$pQpWFxTX+6 z>-gYDHC~AOdyLsD(K}M4yVw zbD}xzJ?(wV`x+Y>fq1MJ(S3i=Z)!H%pK|%%3c1#JQVlX_C|8NXCS4%abs9AFTA5LZ z!^z!>#-(t_mI?HuBSTaRij}IZ(<--X=+h~5>)fX@yUa@#FCl!wsBO$i%t9m) zVAPX@-JK!At);pAUgkXF0Wf>0d3Cs=+u2f+DXgkYYXBxf9B5&RO=0Qrw+%%5(hMFJ zRfigy6vixFMwzM5r}c6<4Z%m)=omPhzq>J7Z!(&&j#L6?g>e#8hPJZ$i(DqpvS$Vz zevj9~x9~VUS=tOLPtrK!^3zOQ^HzEJKgP&45psznh3|-XM59azDFh(ya+6ACdGqEr zo|5Y0yLct53{u57?%PtsWL4by8@3OXgzMaTI`N3N78gB7r-)X&I&>(%v=ohAP+4hy^;PRHe;K@dSvk}hw4}Ir=ia>=jvt@@ z)?4$%xTZ`daX1)3DKO4)-DbFEf-+96!{l5O-N1Q5ix62vd^kBnrjam+CWe;%c*&7U zRVgVBiNt6y_^sAFa_DpcM5Zy)VlnzGV<(Vo?h=3DgzYc<<~P><&;Rd?$TiVJsATec z-49%o#G+y0(wgwbM)!^;%aQH<%lejV>c~3PnsRJ?eOq%QTr=X3Hif165g&^mZrQSh zSY3B})8Ycjz*-Wq#Dc3;WLqn*Q;AEw)OAs zuWPC!Zw^?i>?-9Fwf49{f7Yb#&}f}HR+Wr#O)B(er)eEpDI{uWqFK`73}6T1NsXaO z)Ns@LlBUYD<%%uB_#uI7Zc_!Lq2{J$yuQ_|S5sd6F2h?j{tRU%murd-V^ABq-L2;` z4zDa&R#76%RU~#J6!x3m-~vdMJBQL2WBDIr7Q0_ZD$bYjvT5+Odx=f@>1<8*a|-T0)4MpqRJiny_0& z_8>FNb3}@qE|?_E>6a;X*<|S;4xyXuKrUG@U2I!am`lXuJ10R2oKjGN9Yh@{0G&if z7w-#ng2AU}Wd&EREa>aY9^##p!JFl+0Wmh*T3(-Q3?4NU6OxJrhpl20(JT(xIB&15 zbwB@n^NAB}OO~wPumKuC_MDFC^iVhd{O22Ad~rW{^B?~3^ixl5*}Rz=xQBufmT$Zf z_{mSy`}WCFQg}i*>y~}{2F{;P@9kxIBEgWTRP&DacUhV=bC6E zE)lTKUn~T2lAw*EtYesw-CN_DHw#K6+J_!oG3&7tsnSxFN{hTgNyU0Fg!D^;dJeBP z;V$EJC|$UKIcquvkDBRYiiCluOh7-oA4C;YHq4yZU<?~lEFGY*x=S6kePo3lBO*fWb z`RGuV)>M7FU;GRG6ESnFPd-a@$6pB3u(5 zS**`H8s6Po&|Okg0$czo1Xp-{+5v+^FoyPl+DEkE1D>td!siZVY%jFuGRBB^R4SAi z!Hj}vJ|zjy5ZB~$&>~n5Di5g>JilJXPbupD3`-P*G93$6! z^wCEV7;@rFqDgXGTr`OMIN5WiHzJ$~2W4U-G06@GjkPTlldGVc3>5-}zyT~TFoMs9 zQosvwXz5SFhEkAbZ13+6z4)U4`0@0zGHfpH8PEjCup5jfZkvzy>KDpt+zV5yIFro;F=oEyn_elEnYmsHDllR4`L@d z*A$H(@`pfU8xk}PJHbh;BYF-yL5>p&a0u7@`af>^=D%K(N*d|pCPwK+CFd^|9r^yu zYy;z*s;lcoGmjq@BNM|PTRd}ra82GtE>{%gX5@uk%NBRhU$eF_xTSpIsrtuP6-&32 zKC!yS5%k#o{=lwXyFe@Grua>xY1*=owxYCPIyS72*4{&(apG~+pccan9f^m&%2!8@s@qNa?jPh zTaIk)>F)ubS&Y%R$ZR}gF+XKf*J~U;2W}WBFSZ+hk@;9GA4i-n4bV+7P{EfXmf;eJJD-EzW^i;)+AX#WYkFHw z)2i^&pxT!*9xGgr1_;B?&v4BIGdUKnnJSq-CtRh;Yb&d3&IowS;hg{4b@suML5Z-a zePTFcc$*JDgL$G?#_;o_ok5CF)zN;vi}PdTnvh6hmZZYTm=mpJMtA6DZ0HZYHsP1V zHJQI1RtmEuZ_b=YghH`IfG{w^jc|8yUqNGbaME~jY$y>;Gy=U~gMd~wH&>lMpRsWx zu}aS1k`cB9h|nWkH8zk0Ljc!AgU~(#KmsqMyNkyoqCIDFySt6=y|?7@<-+pvb7h{XqD85{`V|XTE`0XcRJ|Tn zz~}5ec1(Bb6x3EkR0SS;Wu<~q>LU?`yJ-B_B{Ol&1ZA9Bhsn7nV&`_dSa1?4N1$UL zRouZmfcrR1h!moXo0U*t=uocHWimUST1{vt6*a(2igtxxxfX?q?@BUK4dZqWS)^+- z5|4$$4~sp)nsx_kEf-rwRUlB{)wXK_wy^RRu!%< zc9y!*LTMzDV!e}jNZJhw4k9Bqh^r&)ly@o_vP0yO#U~3+7p*wn-@2o%YH_vK=Ouy^AR-p<6f}ZY8hLsofB|Q(Uxbbl90jUP>;%+`5zVUGLl;rEdHL+Pq3f$>WW8|8M z^xU~~F)N5PlFUFKwuc{n7+Og_otULaLrI|1p%c+!K})zPHkyrTj|h%nE72O%A2dcO z@LB<9G=gvsFoM_0Mi`}vnK1?im<-9`cH^OmxplEHEE5$&(Xib@Ae7nKGcnK>=x6Bo zanGt%OzF-cl`{vDJT1*ZywhOFee+G0GSAzy=hXT0CA)T+-+%wil`9a<4I4I`y>KDz z!w==Jzb+318XFpXKmF+wAAdaeJKvGYWLR5tdDZ64>a}Z|SFV%{B^=#5AFR zcxX1$Z<;AybfTe+2Le=WQr z-EP}xV=pl2T$4GTI=edSyX%`)HTP{?>W>8E8abXlWC+2IaUGBsaswhGHk`Ky?DAG6 zS#r&BJ(D8Uo~d7dY(w4V`l8Mvhs(iBG2|su9*NXP7+sVR&fr>fL~Ir$P_z)@A>hcb z$RtmK!GOyO=j}4hpk94gr#h^yAFPjLMF4c@FP)_jS~_=v;6x_G8j}B=B5_Tl+`s~r zPnDi*C>ADx!u%kI=)Ov;&NXD6$k?+seYsOF%)Q4+z=Q^g@56v$&hkYDb5<|+_u4g9 z8ju3Sr7)8iPPjYYi`E-o;Y{AJv0rDnW`aE%BiBT^LP39i&j#h%mlPS*_QD#4&3D&B z(P3IhRP3W9-;v4`kEPd}Gm4WJyQ5#n$Tg`GYs-X3Y~s*z7$ki(85{J4FMI)7NsN=& zC;4<(Cb3V>(7fc%X74Cg9F1)Bdg1g?oCkj`n)j^i(csui4pR@2m{3E=fI9_3-{q_x|ncK_AR{cUwb6 zUeKav77L-B3JdakORQmAYHSvy+qT|q3rsH8L?&4V9b!#Pv7x@9qrC&xLMtKFL?S`w z7}tbiBH^?i5e0#3I_%0iwehrtsW)xcTAH8fWec6%Gv=Rxtr=g|2Y3VwDMm9^9 zs?~`T>W%$-p;Pf8jN}*2G1&jlWv@Sy_R0J^$xDyzSF_0&@TV^yo@@xlwUe)h8k+1X+Xl$4bP%gY&~G`7|qYpEHonV^hQ>o5t|WXgj1 z^Rnj8uXZ}KFu;Ul;=eg?!AZCa1{nVd#E&t^_+soM)+mEWC=q>d0UIoqL6=Yu)F$DQ z2|7d;ar?L_x`Z!y+q>K!Tyw63NuKyzLWc*Mx;dga(r$isZTP?@h}M$*8+!U0bC(n- z&$KMuv8-zI#tqD)M%)PWF+zzx8D>$OpS!IxCEvL~IW&O!woNyd0zA?r=FkbDiSx|C zH9=cK0 zuQOaT!JgfoYf2?g7}91N(=xJiL1QL*#2G_bZU*)5>LcAzTywt0@Wrm@AF+;G4qc*D zKNxKIO38t_io~@}65a-u34ig75~Ymf~9 z#BJj%w6?WjrU4~9JxazuywPG=wt98OmMxKY-Z}K_v!$=SR{f(N?YVU6^x3mluU`vNx0_Fhi8@qwbLm~c!Uv+7X`-A+H5YHO_)3xdx?ET*5eDr_K|4> z8Z^OdpcTk*d{hPnK<-DoHde{F>_C^rxF*5Yb4#-3wrW3_Wz%~_W+OU zy6%0Gm)!S#H#g5O_r|`yzRq)Ezt~Bv#BpL{RE2%@n8?7er1B1Ky)q6#HaA}LYblHYH5s1Y;(215`6DLtHzA9&`RefBwLowfe! zzt`HujU}MyEEv(4tJ||Ce{_A_miD5K)`rbP{e!jom6>iuNg)FpdkCQkp+0zk^g%=u zDS-}pgJD^mLuvS;@5cO^yb)RlzvN}{cJN3rnhZ6^f#&gC6TPKTgiO$0G{@f2-Y>$B zo(CnH%Qo!oYiVl<2o6BWp-|`y4gjpDFqlr%0n=b#P&Vq}z645zpG4z`H*g_$Cjh`% z9)OOEF6AosectiMlB(NknBj;gq&bI6AgD=8fXBA*1e0kpwV9(P)59Gu$GuHf|05me znXxIMCm;f)}eW&(~hE9Yh&AKvX2FqqH#FcSKB^qVto3#6CI8ib1eUfpNl0*&$W} z&fr5aBA^4)U?0@tFZPZogfZz%gh@*bR>vvI3mXW7tECt=#Xdkpd59(hcd#QGu@FtH ziQ@nr`)1K`34_hGv9b00_ct6q>~-mq@y?yWW5;&w-Fx)ZDWcO6#nKBu@ zZN8*7U!I%s|Dnq~Di%&8$(E$sx1DS9!J_>fh6i2N&g}_7!^#7s|F2 zIIdq-v}0uJp2nb#f`IT~U%J=8WqHQR@@dSFrwI z$l9f!b`z8IRfUuHEG8RthBtc5(gm!m?4_rlF$Y~!XOxnp$9rU`(3!u zcJX4$$Vkp>uN^vZBH_(91Mc3f+p=ZV<;%vw!G^lJ-Me?M?(bi=XOGvlYYM%dhgrRP zHBUlwntG@ll%v7*thQ6Ss;IW6Wc}E(ZJDvxU$8Zns!e=<6Fm zpq>J7%CTk4`_w=@0~EiX7{t(rFGtG~Z~-9i9ZAjhPXM zMK3AOV=$J3|0lfm+s-xJokb0ii@WmF33@-Kv=AD&6=fy;#qPcN&U@REwhyfz=bq&954=97(UqAPUYU>+i3mVEAz;E?rIox8KQbZT2SYS$i4 z-r1MGexRUdB&t8dHwDcK3JhA&u)L|FF(xd^&*MR6{J~C$s%)H)53nIT=u={SC+V$ zdJxXU#*<9!{>D}=%y|T4v4d?cUAlDs{P_$&XO$p0C7Tf7{NWFO2qmSDCe)Jxyc)Pc za738=2*d!az+{LfAO^hPUMa(I4swJIVz<1tHLI|Y&X+hl5C&=sE>Q{PfiLb3n!)lp z1^bMRiOC%ps5^9sPS7ltAUv}`H*wrLt(MFJOf(`aOjTb$bo_WT_YVwIuUp6TGz_)u=;){@FX!3#QaE4k3!1SrRx&fP zKgTsKjPdFEIVIPmA%Yh~;ViVl(b0t-!%WPIiW7uKWvy^cb6k@$WEchB`Jh6_XP+(p z-EWUeB#lz(*d;D4?lv<&xjP;UEyuC!bY=EAq3|w>b=FJTJ6u2f=aTZwTFKiWM1IOW!LTyw6&nNO%P9)r zOYjhQ1@N6>cn)YKt$+UVklYu#$?T_ez#=Ke{3sRm|NPHn%o&Q#z;oCn)RUenoI;qipPA;JCL$x#p`MUV3Udk9 z@|f&{U0?-9!3NII&yr3l00oI@TA?(Cg+4sB-QAT(j_9kaGxG9~50HhxY03gPKocGr zcL3Z0K8TKldg9itE$7ci=jVrMwM0xYD}j#!KAzFh4lliwv}FsoVU+svLx-A=A8)*T zS%3F#{#$Pa-@o5};6Tsta8qOB#$CId?%rL0-~c8J#f0zDYt4B2bnCKZ0)-HgiU20{ z&$!g*xTb|MK3zYj331Ke{HE$3f42X-$~6(VQZ7-`>&?XlG8E|R(UvUQo;SZQ zeaYqm@sVwvtp327V=XPsIP%nZrUY!oH{3^gZ1L{&%(zDOki zIu-#XqAHGn;B5)6h2krr4JO>RnE`ek52_TwGYl7!s;ZXkKONeno zx!8yA;tXv026&4n7}rE-zEbZszRM0oZ!3t%@o;z7duaOtJ5K5LZPWJ;MtALs+qAD} zdwatwW`QOyB;?mfva|6~%y@!WcyUNJ4iZV~LbR*Gn|JyTwuLlD1{o0-qGk$xYOqBn zGh1ekYg+g-Tev2l0ELGqkrRMxl1uh9B#8o|#m;K;V8kgtlUVetg!=!h{#8d!7F<(k zq)l-NA$EmwLE$D+Z(;&?TbiTb3>Lw=10C!jPv8Z?Qk$oTN7}Mw z#plmwuUnUpnhJ1%OPC2nle2_cD2Yb^9|Yh6ru50l-J3QsXA-9-P@y`X#dcE0%H*5k*cZr+S}=bg3t_jmX9vRZm&RaMlLE8C781HLNC z%HpnHm%aYF*X7Hdt5cDJvCnZ$3;lVzeoo0X?d_5s?3-k=iF;m~ z+=;I*zS&A#6Q6+xr5LJ4c{CmkM$5mnMloR>bbZD(@5}nX`k!0CbWwV^rr^nN@lq+_ zkcqw2w>_@;)tf)q^HYo0X37qf*$tKXZLTvO**S3J$Pu~&Hdd7`ugvPrbJI-N0SyR)h%9S=AtKsI&a00fLSMGKhL$K9R60!_!$P%}7gR&`*V z8(|&jB!rcHYT!ct(tg9NNb&F5wTqM!-S%O|+>!e-r`=e>YVn+7e?Aee$=eAph)0Z% zjq?_HCV9qrDBYowN|{TcXZFGP(G>}eNt%RJ#+JQl+j^IE#m2;P2{J^@cvJcbAZ^4Y zybJsIC%zPtqzr#kf|CY<*VXuzvQ#?KOG-|P2+$f_b)Is|uQpa1-^BhL*R=3wHgHWE z1*Vu8w#K+OMt9Ms^6>CvI2l!5Lf9-!q~-s5G52Nsc2WM*PrylZQv80Sz>tTM*-fbV5Km`;(vignbHs|NGyE zbPAD9j_{^DJT45Di7fyZG<`u8{(@a#4h5(-Ei0?GwY9RYE;1^Lb{gSNPzb;Q<%E&~ zo(pu7yZZ+QcCKB^uyHDYLc-?id61gUhFWv>&6A%O}0>cZ_SgDnx5i z9e1`R@83Sy*VVaq&#q&~jtw*>4CUAjmAG%oUAirMes!{sUTc~FlGd}qPwMF+(rLpH z7vC3vp4pGW=Xg?FlNSVk2}zQb2365O)R-tEKul~>2sc{1ECB;(4FNXT9uSYtvu+Kv z50E9+2;vi6fFlY48(HNJh$m!G zIBrDV9H57C(Z`gzHn|tn&Eg5i|HP(dIj*U2lKU%sb-pZF5{sqF6^dX*z}n!}n!r+- zRK_a@`3B{u<}v?KfPX+mdfBe5&7+w^k>RGX6eLBvLYo@xHAk0eo26nd^-7YlI9#FgvnWkqC+&<&@tF20a1dQ)Bww* zlPl%`b^$nWLEeSGY&)r%!LYa6xtJR{wCt#eH*XYr!w#f$1?QdXOtpzkx9ic+G)==WsS&T*UsK3MRMuog5Doil3_ zK86D5wE4(4w}UluBMgsQ0B~>%EBE@2aZQE_tx2{UT^_%?tLAh^^sAk2x4Ts*YQ@`g z=PgTe8Yy!<+2q!ou)Pr#I)@qM+b-(yoV!J1U0==D8>`vn%oy$hP^S15U7pr zp^$_G&<#Kql@Jn47B7W=0-)&>MYxg6p?#1)qQVT%glnQpT*c)?D5(dQ$zOCHmWhD~ zx{dx5zz}#RmGc-v8Hgj|9Dr9uKYw`je9)u!rry!#K;` z#4p(}5E`Y31(pehkPl(TB6@z)LI%u{809fT01e=Bh6e86ueo$-{pcvXA8$^zz#uJf z1jUmRj0taOP~5${<_wbwFVDPmY0WFI96x*Z%$YL>k01AV_0{f|URr+c9E{UwFwm`> zR7^~CG+fi=!w&9?S9MK|9}m#gC7)Y2$o#SY|FAO!*ED%VTpB!M z;+U359KLsTgERg01YCVmnt?fWyE{Qeb(r69yv{)2w z%zSG6&UcJ!0vX<}lKMpTzDoJIdWXx+cCW4!9qUNkw=!>SV&(%z)4QOY0A1??tNrH7=X zN2Ur&f}o6%#?Y`(-ld|fqI<>qn(QifwTWtQ3Qa?dhy_+Hlm{009K1Cwp8^OLBmpiE zn#pxNm}H6_nUjo=>fAW=C+@SR;+l@n#5O(C{L+utzx#joefDQvfBId^?IqyBgM{pn9A^U$g}IGjmUQdnFMO03Cp47uWYrlz#FR;RQeX<; z!Xn_E&`qc(nM1Gz@Zl6l0@36+?}#-)H-S6wgti<7Fdr@ioxmupi785Ab8-rsng$-A z8CeuMu>dkyB2|HDnHd?z=H~pXS2u3o&LnZGy1I7k+eZ`=u6g>@smx8Ayzk!^fAUGu zxpOSlu&T2&<>pOePtURyE4)TW?LPk4;odz_O^w*mk+h~?QBgrv6~d>hsrt*A;3gB- zqaL4&Yd&%FxAp*2a!sq>TQq-O^6!2-8W_+U5D<>pOja2EMyDYz6u=&!GGl!?X$9Q; ztZ_}a0``e{V@Ot>nMeJOaZQm}66Mh$S9HPfk8s`_>1EK zm%%|qF?}He8D! zF1Y4HtbZ_s2(Q3d82o8)Ha)O zP2NlDM5j!pi_Ft z8%ZJxK}vW~paW))1cc9osBx1BmKYC;YG$R=Q^Lo*YA&v6;a#TSnk3NW%K4h)|9j2V zKOg+lpLPG~XAOU_%h@c}<(pP4v|-vHi|-|pDt_4d!hc(K&n{y~B74}pu_!b4r;uAf ze~9WqyfV_$QOfzAA!KmI7MsStNTG5FO&3SxS;@%LK@*3|wC#-YA(IYwWatllF`<