diff --git a/.gitignore b/.gitignore index be3c65e..072e8c8 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,8 @@ manuscript/** !manuscript/**/*.bst !manuscript/**/*.cls +!manuscript/**/*.sty !manuscript/**/*.md !manuscript/**/ +!manuscript/graphical-abstract.html diff --git a/CHANGELOG.md b/CHANGELOG.md index f041deb..d37bdc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 1.16.1 — 2026-07-22 + +### Fixes + +* **Disconnected nodes stay hidden across filter changes.** With "hide disconnected nodes" on, changing a filter could resurface a node that should have stayed hidden, and nodes that became disconnected by the change were never re-hidden. The dangling set is now recomputed against the current filtered view on every change. + +### Features + +* **Checkmark badge for single-value numeric filters.** Numeric properties with only one value (min === max) have no range to narrow, so they now show a read-only checkmark + value badge instead of an inert slider. The row's include/exclude checkbox still works. + +Also includes manuscript revisions (docs only, no app changes). + ## 1.16.0 — 2026-07-22 Saved graph files load unchanged. Stored bubble-set knob values keep applying, with two semantic shifts: padding and corridor width now scale with your configured node size (a value of 1 ≈ one node radius of margin) instead of absolute pixels, and any saved avoidance value above 0 now simply means "on". diff --git a/MIGRATION.md b/MIGRATION.md deleted file mode 100644 index cba5c48..0000000 --- a/MIGRATION.md +++ /dev/null @@ -1,171 +0,0 @@ -# Migration: AntV G6 → Sigma.js v3 - -**Status: COMPLETE (v1.15.0, 2026-06-10).** All phases (0–6) landed on -`feat/sigma-renderer`; G6 is removed. This document is kept as the -architectural record of the cutover. - -Full cutover of the rendering stack from AntV G6 5.0.48 (canvas) to Sigma.js v3 + -graphology + bubblesets-js. G6 is removed entirely at the end; no dual-renderer -period. Work happens on a long-lived feature branch (`feat/sigma-renderer`) and -merges when the parity checklist below passes. - -## Why (measured 2026-06-10, 6000 nodes / 9000 edges, AMD Radeon 890M) - -| Metric | G6 canvas | G6 WebGL (`@antv/g-webgl`) | -|---|---|---| -| Initial load → rendered | 1.6 s | 55 s | -| Full `graph.render()` | ~10 s (includes layout) | 88–205 s, degrades per call | -| Pure redraw `graph.draw()` | 0.65 s | 90 s | -| Wheel zoom | **~4 FPS (~500 ms/tick)** | unusable | -| Warm drag-pan | ~163 FPS (fine) | unusable | -| First pan/zoom after load | **~11 s stall** (lazy init) | n/a | -| Select 500 nodes | 0.14 s | 21 s | - -- G6's WebGL renderer was spiked and rejected (10–100× slower than canvas). -- G6's `optimize-viewport-transform` behavior was tested and rejected (zoom 4 FPS - with vs 5 FPS without; it hides edges but nodes dominate zoom cost). -- The canvas pain (wheel zoom re-rendering every node, 11 s first-interaction - stall, 10 s full renders) is inherent to G6 at >10k elements. No tuning path - remains on the current stack. - -## Target stack - -| Concern | Library | Notes | -|---|---|---| -| Rendering | `sigma` v3 (WebGL nodes/edges, canvas labels) | MIT, actively maintained | -| Graph model | `graphology` + relevant `graphology-*` utils | replaces G6's internal data store | -| Bubble sets | `bubblesets-js` (standalone, MIT) | the SAME library G6's plugin wraps — outline geometry is renderer-agnostic. Drawn on a custom sigma canvas layer under the node layer; issue #7195 disappears with G6 | -| Layouts | keep `@antv/layout` headlessly (positions in → positions out) for force/radial/concentric/mds; circular + grid as trivial geometric functions | layouts were never the perf problem; replace opportunistically later. Alternative: `graphology-layout-forceatlas2` (web-worker build) for force | -| Node shapes | `@sigma/node-square` + texture-based shapes via `@sigma/node-image` (rasterize hexagon/diamond/triangle/star as SVG textures); circle is native | custom GLSL programs only if texture quality disappoints | -| Curved edges | `@sigma/edge-curve` | maps cubic/quadratic; polyline degrades to curve (note in UI docs) | -| PNG export | `@sigma/export-image` | replaces `graph.toDataURL` | -| Vendoring | esbuild bundle into `src/lib/` (same pattern as `src/package/vendor_libs.js` / `build` scripts); removes 1.12 MB `g6.min.js` | sigma+graphology ≈ 200 kB minified | - -## Concept mapping (G6 → Sigma) - -| G6 | Sigma/graphology | -|---|---| -| `new Graph({data, node.state, edge.state, behaviors, plugins})` | `new Sigma(graphologyGraph, container, settings)`; states → `nodeReducer`/`edgeReducer`; behaviors → explicit event handlers; plugins → custom layers / DOM | -| `updateData / updateNodeData / updateEdgeData` | graphology `setNodeAttribute` etc.; sigma re-renders reactively (`refresh()` when batching) | -| `render()` (async, layout+draw) | layout is explicit (run algorithm → write x/y attributes); `sigma.refresh()` is sync and cheap | -| `draw()` | `sigma.refresh({ skipIndexation: true })` for pure visual updates | -| `setElementState(map)` | maintain app-level Sets (`cache.selectedNodes` already exists) + `refresh`; reducers read them | -| `getElementState(id)` | read the same app-level Sets — G6 round-trips disappear | -| states `selected/highlight/dim` + halo | reducers return modified `color/size/zIndex/borderColor`; "halo" via border program or a bigger ghost node — decide in Phase 2 | -| `hover-activate` (1-degree) | `enterNode` event + `graph.neighbors()` + reducers (already gated by `DISABLE_HOVER_EFFECT` thresholds — keep them) | -| `drag-element` | `downNode` + mousemove → `graphToViewport`/`viewportToGraph`, standard sigma recipe | -| `drag-canvas` / `zoom-canvas` | built-in camera; `animation: false` equivalents via camera API options | -| `lasso-select` | custom canvas overlay + point-in-polygon over node viewport coords (no library dependency; ~150 LOC) | -| `click-select` (shift-multi) | `clickNode` event + modifier check | -| tooltip plugin | pure DOM positioned via `graphToViewport` on `clickNode` — `cache.toolTips` content generation is unchanged | -| minimap plugin | custom canvas layer rendering node positions at thumbnail scale (~100 LOC), or defer to a later release (decide at Phase 4) | -| bubble-sets plugin (4 groups) | `bubblesets-js` fed with node viewport rects + avoid rects → path2D on a `beforeLayer` canvas; group labels drawn on the same layer. `GraphBubbleSetManager` member/filter logic is renderer-agnostic and survives | -| `fitView / zoomTo / getZoom / translateBy` | `camera.animatedReset / camera.setState / getState` — also deletes the G6 zoom-at-non-1.0 workaround in `core.js` | -| `toDataURL` | `@sigma/export-image` | -| `setElementZIndex` (drag z-fix) | `zIndex` attribute via reducer; G6 workaround deleted | - -## Phases - -Each phase ends with tests green and a working app on the branch. - -### Phase 0 — Scaffolding (small) -- Branch `feat/sigma-renderer`; add deps; extend the esbuild vendoring so dev - serve (`npm run serve`), Electron, bundle, and inline-html dist all load sigma. -- Commit a benchmark fixture generator + Playwright perf script under `scripts/` - (recreate: 6000 nodes / 9000 edges, 8 clusters, deterministic seed; measure - load, wheel-zoom FPS, drag-pan FPS, 500-node select). These are the - acceptance gates and prevent regressions during the port. - -### Phase 1 — Data model + core lifecycle (medium) -- `Cache.iterNodes/iterEdges` populate a graphology `Graph` (keep `nodeRef` - Maps initially; collapse later — graphology attributes can become the single - source of truth in Phase 6). -- Rewrite `GraphCoreManager.createGraphInstance` (`src/graph/core.js:166`) and - `decideToRenderOrDraw` (`core.js:111`): render/draw distinction becomes - layout-vs-refresh. The `AFTER_DRAW`/`AFTER_RENDER` event-lock choreography - simplifies — sigma refresh is synchronous. -- Port `GraphFilterManager`: filtered elements get `hidden: true` attributes - (sigma reducers skip hidden cheaply) — same Sets (`nodeIDsToBeShown` etc.). - -### Phase 2 — Visual parity (medium-large) -- Node programs: circle native, square package, hexagon/diamond/triangle/star - as SVG textures via node-image. Per-node `type`/`style` from - `GraphStyleManager` maps to sigma attributes; per-layout style Maps unchanged. -- Edge rendering: straight + curved + arrows; lineDash needs a custom program - or is dropped for v1 (flag in UI docs). -- Labels: sigma's label density system replaces `MAX_NODES_BEFORE_HIDING_LABELS` - (keep the CFG as an override). Node + edge label styling parity. -- States via reducers: selected (border/halo), highlight, dim — colors from - `config.js` state specs. - -### Phase 3 — Interactions (medium) -- Node drag (with position persistence via `lm.persistNodePositions`), - click/shift-select, hover-activate with existing thresholds, lasso overlay, - hotkeys (renderer-agnostic already), tooltip. -- Delete G6 workarounds: lasso/canvas-click event juggling - (`APPLY_BUBBLE_SET_HOTFIX` paths), dragend zIndex reset, zoom-translate quirk. - -### Phase 4 — Overlays (medium) -- Bubble sets: custom layer + `bubblesets-js`; wire `GraphBubbleSetManager` - (member sync + style UI survive as-is; only the draw target changes). - `avoidMembers` becomes cheap — re-evaluate the - `MAX_NODES_BEFORE_DISABLING_AVOID_MEMBERS_IN_BUBBLE_GROUPS` threshold. -- PNG export; minimap (build or consciously defer). - -### Phase 5 — Layouts (small-medium) -- Wire `@antv/layout` (or FA2 worker) behind `GraphLayoutManager.applyLayout`; - circular/grid as geometric functions. Custom layout = position Maps, already - renderer-agnostic. Force layout for authored payloads without positions - (see commit b6f8606) must keep working. - -### Phase 6 — Cleanup + release (small) -- Remove `src/lib/g6.min.js`, the `G6` global destructures, `APPLY_BUBBLE_SET_HOTFIX`, - stale thresholds; collapse duplicate state between `nodeRef` and graphology - if not done earlier. Update README/API.md/CHANGELOG; bundle-size check - (expect ≈ −900 kB); minor version bump. - -## Files touched (from the 2026-06-10 audit; ~60 G6 call sites) - -| File | Work | -|---|---| -| `src/gll.js` | drop `G6` destructure; graphology instance in `Cache` | -| `src/graph/core.js` | full rewrite (instance, events, viewport, states) | -| `src/graph/layout.js` | layout execution + position persistence rewiring | -| `src/graph/selection.js` | swap state round-trips for app-level Sets + refresh | -| `src/graph/bubble_sets.js` | draw target → custom layer; logic survives | -| `src/graph/style.js`, `src/graph/filter.js` | attribute mapping only | -| `src/managers/ui.js` | behavior toggling → handler enable/disable | -| `src/managers/io.js`, `ui_components.js`, `metrics.js`, `api_client.js`, `query.js` | `getNodeData()/getEdgeData()` call sites → graphology reads | -| `server/*` | untouched (verified renderer-agnostic) | -| `tests/*` | new unit tests for reducers, lasso geometry, bubble-set layer, layout adapters (≥80 % on new modules); existing 622 tests must stay green | - -## Acceptance criteria (the merge gate) - -Perf at 15k elements (the Phase 0 harness): load ≤ 2 s · wheel zoom ≥ 30 FPS -(currently 4) · warm drag-pan ≥ 60 FPS · no first-interaction stall > 500 ms -(currently ~11 s) · 500-node select < 200 ms. - -Functional parity: 6 node shapes, edge styles (minus documented lineDash/polyline -degradations), labels + thresholds, all 4 bubble groups incl. styling UI and -filter/manual membership, lasso + click + shift-select, hover highlight, tooltip, -drag with persistence, all layouts incl. custom, Excel/JSON import-export -round-trip, PNG export, SSE live ingest (`?session=`), Electron + inline-html -dist builds. - -## Known risks - -1. **Shape fidelity via textures** (crisp at high zoom?) — prototype hexagon in - Phase 2 first; fall back to custom GLSL program if needed. -2. **lineDash / polyline edges** — no off-the-shelf sigma support; scope as - documented degradation or custom program. -3. **Minimap** — custom build; defer if Phase 4 runs long. -4. **Excel style round-trip** — exported styles must map back losslessly; - add a round-trip test early in Phase 2. -5. **Icon/texture-heavy styling at scale** — sigma's known weak spot; the - benchmark harness catches it. - -## Next-session kickoff prompt - -> Read MIGRATION.md and start the Sigma.js migration: create the -> `feat/sigma-renderer` branch and implement Phase 0 (deps, vendoring, benchmark -> harness with the acceptance gates), then continue with Phase 1. diff --git a/manuscript/2026-01-12_ADPKD_model_GLL-input_GLL-paper-version.xlsx b/manuscript/2026-01-12_ADPKD_model_GLL-input_GLL-paper-version.xlsx deleted file mode 100644 index 3e43705..0000000 Binary files a/manuscript/2026-01-12_ADPKD_model_GLL-input_GLL-paper-version.xlsx and /dev/null differ diff --git a/manuscript/2026-06-26_ADPKD_model_GLL-input_GLL-paper-version.xlsx b/manuscript/2026-06-26_ADPKD_model_GLL-input_GLL-paper-version.xlsx new file mode 100644 index 0000000..1ef07fa Binary files /dev/null and b/manuscript/2026-06-26_ADPKD_model_GLL-input_GLL-paper-version.xlsx differ diff --git a/manuscript/Fig/adpkd-figure.png b/manuscript/Fig/adpkd-figure.png new file mode 100644 index 0000000..72b347b Binary files /dev/null and b/manuscript/Fig/adpkd-figure.png differ diff --git a/manuscript/Fig/main-figure.png b/manuscript/Fig/main-figure.png new file mode 100644 index 0000000..c9de97a Binary files /dev/null and b/manuscript/Fig/main-figure.png differ diff --git a/manuscript/README.md b/manuscript/README.md index cd72434..bedcff0 100644 --- a/manuscript/README.md +++ b/manuscript/README.md @@ -1,12 +1,32 @@ # Graph Lens Lite — Publication -Multi-venue LaTeX setup for the Graph Lens Lite paper. The body content lives in -`shared/content.tex` and is rendered by two venue-specific drivers: +LaTeX source for the Graph Lens Lite journal paper, targeting the OUP +`oup-authoring-template` class (Nucleic Acids Research by default). -| Directory | Target venue | -|------------|------------------------| -| `oxford/` | Oxford Bioinformatics | -| `biorxiv/` | bioRxiv preprint | +The folder is flat: one driver (`main.tex`) plus the prose split into small +`\input` files so each piece can be edited on its own. + +- `main.tex` — driver: preamble, frontmatter macros, `\input`s the body +- `body.tex` — the body, Introduction through Conflict of interest +- `abstract.tex` — abstract prose (no heading), fed to `\abstract{}` +- `keywords.tex` — keyword list, fed to `\keywords{}` +- `reference.bib` — bibliography database +- `oup-plain.bst` — numbered citation style +- `Fig/` — figures + +The body is in IMRaD order (Introduction → Materials and Methods → Results → +Discussion); headings are unnumbered under the OUP class (`unnumsec`). + +## Author conventions + +Look for these markers when reviewing the draft: + +- `% AI-DRAFTED -- REVIEW` — prose drafted by an AI assistant during the + manuscript restructure. Treat as a starting point; revise in your own voice + before submission. Anything not marked is user-original prose preserved + verbatim from the prior draft. +- `% TODO` — structural placeholder. The heading exists, the body is empty, + the comment block lists the angles to consider. ## Prerequisites @@ -56,86 +76,49 @@ sudo tlmgr install natbib acronym preprint lineno enumitem lm ## Building -Each flavor is compiled from its own directory. A full build requires -`pdflatex` → `bibtex` → `pdflatex` → `pdflatex` (two extra passes resolve -cross-references and citations). - -### Oxford Bioinformatics - ```bash -cd oxford/ latexmk -pdf main ``` -Output: `oxford/main.pdf` +Output: `main.pdf` -### bioRxiv +Uses the OUP `oup-authoring-template` class (`webpdf,contemporary,numbered`, +numbered citations via `oup-plain.bst`). Set `\journaltitle` (and `\appnotes`) +to the target venue at submission time. + +### Preprint / bioRxiv version ```bash -cd biorxiv/ -latexmk -pdf main +latexmk -pdf biorxiv ``` -Output: `biorxiv/main.pdf` +Output: `biorxiv.pdf` -### Build both at once +`biorxiv.tex` is a second driver that reuses `body.tex`, `abstract.tex`, and +`keywords.tex` verbatim under a plain `article` class — no OUP template +needed. Drops the journal-specific frontmatter (title/DOI/vol/issue, the +received/revised/accepted dates, two-column styling); keeps numbered +citations (`natbib` + `unsrtnat`) and line numbers. Edits to the shared prose +flow to both PDFs. `\ORCID{}` is a no-op here (bioRxiv collects ORCIDs in its +submission form). -From the manuscript directory: +**The OUP class must be available.** It ships in recent TeX Live +(`oup-authoring-template`); if `latexmk` reports the class missing, copy +`oup-authoring-template.cls` and the `oup-*.bst` files from the OUP template +() +into this directory, or build on Overleaf. -```bash -for dir in oxford biorxiv; do - (cd "$dir" && latexmk -pdf main) -done -``` +Note: the OUP class does not define the `description` environment — use +`itemize` in the body. ### Clean auxiliary files ```bash -cd oxford/ # or biorxiv/ latexmk -C ``` -## Adding references - -`scripts/add_reference_to_manuscript.py` (in the repo root) fetches a PubMed -citation by PMID and appends it to `shared/reference.bib`. It requires only -Python 3 (no extra packages). +## References -```bash -scripts/add_reference_to_manuscript.py -``` - -The tool shows the generated BibTeX entry and asks for confirmation before -writing. Duplicate cite keys are rejected automatically. - -## Repository structure - -``` -shared/ - content.tex Body sections shared across venues - abbreviations.tex Acronym definitions - reference.bib Bibliography database - Fig/ Figures - -oxford/ - main.tex OUP driver (structured abstract, journal metadata) - oup-authoring-template.cls - -biorxiv/ - main.tex Preprint driver (article class, line numbers) - -../scripts/ - add_reference_to_manuscript.py Fetch PubMed citation → BibTeX (Python 3) -``` - -Edit `shared/content.tex` to change the paper body — both flavors pick up the -changes. For small venue-specific differences inside the shared content, use the -`\ifbiorxiv` flag: - -```latex -\ifbiorxiv - Supplementary data are available at \url{...} -\else - Supplementary data are available at \textit{Bioinformatics} online. -\fi -``` +Add entries to `reference.bib` (BibTeX). Citations render numbered in order of +appearance via `oup-plain.bst`; NAR style shows the first three authors then +"et al." — append `and others` to an author list to trigger it. diff --git a/manuscript/abstract.tex b/manuscript/abstract.tex new file mode 100644 index 0000000..3ac617c --- /dev/null +++ b/manuscript/abstract.tex @@ -0,0 +1 @@ +Biological network visualization together with graph-based analyses are key techniques in systems biology and network medicine to detect patterns and generate hypotheses regarding disease pathobiology, drug target identification, biomarker prioritization, and digital drug discovery. Network representations provide an intuitive way to communicate and share research findings. We have developed Graph Lens Lite, a browser-based tool that combines rich visualization with a streamlined interface for exploring and sharing biological networks. It offers an expressive query language, topological network analysis, interactive filtering, visual grouping, customizable layouts, a data editor, fine-grained property-based styling, animated edge-flow visualization, community detection, and a context-aware locally powered AI assistant particularly suited for exploring molecular models of disease pathobiology or drug mechanism of action. We demonstrate its utility on a curated network model of autosomal dominant polycystic kidney disease. Graph Lens Lite is open source, with a live web version available at \url{https://delta4ai.github.io/GraphLensLite/}. diff --git a/manuscript/biorxiv.tex b/manuscript/biorxiv.tex new file mode 100644 index 0000000..0c213d1 --- /dev/null +++ b/manuscript/biorxiv.tex @@ -0,0 +1,75 @@ +%% bioRxiv / preprint driver — template-free version of the paper. +%% Reuses the shared prose (body.tex, abstract.tex, keywords.tex) verbatim. +%% The NAR flow (main.tex) is untouched: `latexmk -pdf main` still builds it. +%% Build this one with: latexmk -pdf biorxiv -> biorxiv.pdf + +\documentclass[10pt,twocolumn]{article} + +\usepackage[T1]{fontenc} +\usepackage[a4paper,margin=0.9in]{geometry} +\usepackage{amsmath,amssymb} +\usepackage{graphicx} +\usepackage{booktabs} +\usepackage{tabularx} +\usepackage{url} +\usepackage[numbers,sort&compress,round]{natbib} +\usepackage{authblk} +\usepackage[colorlinks=true,allcolors=blue]{hyperref} + +\usepackage[mathlines,switch]{lineno} +\linenumbers + +\graphicspath{{Fig/}} + +% OUP frontmatter macro used inline on author names; no-op here (bioRxiv +% collects ORCIDs in its own submission metadata). +\newcommand{\ORCID}[1]{} + +\title{Graph Lens Lite: A browser-based tool for interactive visualization and exploration of biological networks} + +\author[1,2]{Matthias Ley\ORCID{0000-0001-6853-8542}} +\author[1]{Kinga K\k{e}ska-Izworska\ORCID{0000-0002-4329-1499}} +\author[1]{Lucas Fillinger\ORCID{0000-0001-5341-8097}} +\author[1]{Samuel M Walter\ORCID{0000-0002-6868-5654}} +\author[1]{Fabio Baumg\"{a}rtel\ORCID{0009-0000-0243-2673}} +\author[1,2]{Enrico Bono\ORCID{0009-0007-7272-5034}} +\author[1,2]{Louiza Galou\ORCID{0009-0008-5176-2802}} +\author[1]{Peter Andorfer\ORCID{0009-0003-3492-4017}} +\author[3]{Pirmin Hauser} +\author[3]{Johannes Leierer\ORCID{0000-0002-7884-9827}} +\author[1,2]{Klaus Kratochwill\ORCID{0000-0003-0803-614X}} +\author[1,3]{Paul Perco\ORCID{0000-0003-2087-5691}} + +\affil[1]{Delta4 GmbH, Alser Stra\ss{}e 23/30, 1080 Vienna, Austria} +\affil[2]{Division of Pediatric Nephrology and Gastroenterology, Department of Pediatrics and Adolescent Medicine, Comprehensive Center for Pediatrics, Medical University Vienna, Waehringer Guertel 18-20, 1090 Vienna, Austria} +\affil[3]{Department of Internal Medicine IV, Medical University Innsbruck, Anichstrasse 35, 6020 Innsbruck, Austria} + +\date{} + +\begin{document} + +% Title, notes and abstract span the full page width; body flows in two columns. +\twocolumn[ + \begin{@twocolumnfalse} + \maketitle + \noindent\footnotesize + Corresponding author: Matthias Ley, \href{mailto:matthias.ley@delta4.ai}{matthias.ley@delta4.ai}. + Correspondence may also be addressed to Klaus Kratochwill, \href{mailto:klaus.kratochwill@delta4.ai}{klaus.kratochwill@delta4.ai}. + Klaus Kratochwill and Paul Perco contributed equally.\normalsize\medskip + + \begin{abstract} + \input{abstract} + + \medskip + \noindent\textbf{Keywords:} \input{keywords} + \end{abstract} + \bigskip + \end{@twocolumnfalse} +] + +\input{body} + +\bibliographystyle{unsrtnat} +\bibliography{reference} + +\end{document} diff --git a/manuscript/biorxiv/main.tex b/manuscript/biorxiv/main.tex deleted file mode 100644 index bb2c2e8..0000000 --- a/manuscript/biorxiv/main.tex +++ /dev/null @@ -1,81 +0,0 @@ -%% -%% bioRxiv preprint -%% -\documentclass[11pt]{article} - -% ── Venue flag ──────────────────────────────────────────────────────────────── -\newif\ifbiorxiv -\biorxivtrue - -% ── Page geometry & fonts ──────────────────────────────────────────────────── -\usepackage[a4paper, margin=2.5cm]{geometry} -\usepackage[T1]{fontenc} -\usepackage[utf8]{inputenc} -\usepackage{lmodern} - -% ── Common packages ────────────────────────────────────────────────────────── -\usepackage{graphicx} -\usepackage{hyperref} -\usepackage{url} -\usepackage{xcolor} -\usepackage{amsmath,amssymb} -\usepackage{natbib} -\usepackage{authblk} % author/affiliation blocks -\usepackage{lineno} % line numbers (expected for preprints) -\linenumbers - -\graphicspath{{../shared/Fig/}} - -% ── Title ──────────────────────────────────────────────────────────────────── -\title{Graph Lens Lite: An interactive biological network viewer for displaying, exploring, and sharing disease pathobiology and drug mechanism of action models} - -% ── Authors & affiliations ─────────────────────────────────────────────────── -\renewcommand\Authfont{\normalsize} -\renewcommand\Affilfont{\small} -\renewcommand\Authsep{, } -\renewcommand\Authand{, } -\renewcommand\Authands{, } - -\author[1,2]{Matthias Ley\textsuperscript{,*}} -\author[1]{Kinga K\k{e}ska-Izworska} -\author[1]{Lucas Fillinger} -\author[1]{Samuel M Walter} -\author[1]{Fabio Baumg\"{a}rtel} -\author[1,2]{Enrico Bono} -\author[1,2]{Louiza Galou} -\author[1]{Peter Andorfer} -\author[3]{Pirmin Hauser} -\author[3]{Johannes Leierer} -\author[1,2]{Klaus Kratochwill\textsuperscript{,\#,*}} -\author[1,3]{Paul Perco\textsuperscript{,\#}} - -\affil[1]{Delta4 GmbH, Alser Stra\ss{}e 23/30, 1080 Vienna, Austria} -\affil[2]{Division of Pediatric Nephrology and Gastroenterology, Department of Pediatrics and Adolescent Medicine, Comprehensive Center for Pediatrics, Medical University Vienna, Waehringer Guertel 18-20, 1090 Vienna, Austria} -\affil[3]{Department of Internal Medicine IV, Medical University Innsbruck, Anichstrasse 35, 6020 Innsbruck, Austria} -\affil[ ]{\textsuperscript{\#}Contributed equally} -\affil[ ]{\textsuperscript{*}Corresponding authors: \href{mailto:matthias.ley@delta4.ai}{matthias.ley@delta4.ai}, \href{mailto:klaus.kratochwill@delta4.ai}{klaus.kratochwill@delta4.ai}} - -\date{} % suppress date - -\begin{document} -\maketitle - -% ── Abstract ───────────────────────────────────────────────────────────────── -\begin{abstract} -\noindent\textbf{Motivation:} Biological network visualization together with graph-based analyses are key techniques in systems biology and network medicine to detect patterns and generate new hypotheses regarding disease pathobiology, drug target identification, biomarker prioritization, or digital drug discovery. Network representations are also a way to communicate research findings and share results with colleagues and coworkers. \\ -\textbf{Results:} We have developed Graph Lens Lite, a browser-based tool that combines rich visualization capabilities with a streamlined interface for exploring and sharing biological networks. It offers an expressive query language, topological network analysis, GUI-based filtering, visual grouping, customizable layouts, a data-editor, and fine-grained property-based styling options, particularly suited for visualizing molecular models of disease pathobiology or drug mechanism of action. \\ -\textbf{Availability:} Graph Lens Lite is available at GitHub (\url{https://github.com/Delta4AI/GraphLensLite}). \\ -\textbf{Supplementary information:} Supplementary data are available at \textit{Bioinformatics} online. - -\medskip -\noindent\textbf{Keywords:} network visualization, graph exploration, disease pathobiology, drug mechanism of action -\end{abstract} - -% ── Shared body ────────────────────────────────────────────────────────────── -\input{../shared/content} - -% ── Bibliography ───────────────────────────────────────────────────────────── -\bibliographystyle{unsrtnat} -\bibliography{../shared/reference} - -\end{document} diff --git a/manuscript/body.tex b/manuscript/body.tex new file mode 100644 index 0000000..bf65dcb --- /dev/null +++ b/manuscript/body.tex @@ -0,0 +1,158 @@ +\section{Introduction} +Visualization of biological networks is a core technique in systems biology for representation and analysis of complex, often heterogeneous, data. Network visualization combined with quantitative graph analysis such as detection of network modules, hub or bottleneck nodes, or cross-talk between networks can lead to novel hypothesis generation or detection of relevant patterns in biomedical data~\citep{ehlersIntroductionSurveyBiological2025}. Biological networks are of particular relevance in digital drug discovery and drug target prioritization and are used in modeling disease pathobiology as well as drug mechanism of action on a molecular level. Biological networks also represent an intuitive way of communicating and sharing research findings with collaborators or disseminating them to the broader scientific community. + +Cytoscape~\citep{shannonCytoscapeSoftwareEnvironment2003} and Gephi~\citep{bastianGephiOpenSource2009} are considered gold standards for interactive network visualization with an exhaustive plugin ecosystem and rich layout capabilities, but are desktop-focused and lack portability or modern web-app features. Other tools such as STRING~\citep{szklarczykSTRINGDatabase20252025} or OmicsNet 2.0~\citep{zhouOmicsNet20Webbased2022} prioritize data integration, enrichment analysis, and access to extensive biological databases over visualization and customization, integrating the network viewer as a helper and not as the focus of the application. Several web-based alternatives have emerged to enhance portability. Cytoscape Web~\citep{onoCytoscapeWebBringing2025} extends the Cytoscape ecosystem with collaborative workflows for network visualization, though focusing on core functionalities with basic filtering and styling. Gephi Lite (\url{https://lite.gephi.org/}) uses sigma.js (\url{https://www.sigmajs.org/}) for efficient WebGL-based rendering, demonstrating the viability of the engine for browser-based exploration, though it compromises customization, filtering, and advanced styling. Similarly, Graphia\citep{freemanGraphiaPlatformGraphbased2022} provides both a desktop and web client with a powerful 2D/3D rendering engine, but focuses less on user-friendly styling, highlighting, grouping and filtering, prioritizing efficiency, enrichment, and layout. + +We have developed Graph Lens Lite (GLL) to address the need for a browser-based tool that combines visualization power with an intuitive, streamlined interface, enabling researchers to rapidly explore and evaluate complex networks without requiring expertise in specialized software. GLL offers an expressive query language alongside graphical user interface (GUI)-based filtering, visual grouping, and fine-grained styling controls. Users can compute network metrics, apply customizable layouts, and map data attributes to visual scales. GLL exports networks as portable JSON files that capture the full application state for seamless collaboration, or as data tables for downstream analysis. + +\section{Materials and methods} + +\subsection{Implementation and architecture} +We developed GLL using vanilla JavaScript with no front-end framework, bundled with esbuild (\url{https://esbuild.github.io}). For graph rendering and interaction, GLL uses sigma.js (\url{https://www.sigmajs.org/}), a WebGL-based rendering engine, with graphology (\url{https://graphology.github.io/}) as the underlying graph data model and for layout and metric computation. Custom WebGL shader modules extend the renderer with animated edge flows and a node-density heatmap layer. Spreadsheet import and export of node and edge tables use ExcelJS (\url{https://github.com/exceljs/exceljs}). The graph assistant renders Markdown responses with marked (\url{https://marked.js.org/}) and sanitizes the resulting HTML via whitelist approach using DOMPurify (\url{https://github.com/cure53/DOMPurify}). An in-memory cache store holds shared module states and the workspace data (coordinates, filters, styles, group memberships, active query and the syntax tree, and view states), which can be serialized to JSON for export, enabling session restoration. While the core application requires no internet access, two optional network paths exist: the STRING demo loader issues outbound requests to fetch example data, and the background service (see packaging and distribution) accepts inbound graph handoffs from external applications. No usage data leaves the client. + +\subsection{Query language} +The query language is based on logical combinations of conditions on node and edge properties. Each clause pairs a property-path (``Section::Group::Property'') with a numeric range (``BETWEEN x AND y''), inverted range (``LOWER THAN x OR GREATER THAN y''), or categorical membership (``IN [..]'') operator. Clauses are combined with the connectors ``AND'', ``OR'', and ``NOT'', support nested parentheses, and are evaluated left to right. The editor renders each token as spans with dedicated CSS classes for syntax highlighting, and validates on each change, flagging empty clauses, missing connectors, unmatched brackets, and invalid property paths. Valid queries are compiled into an abstract syntax tree that employs functions to evaluate each element to drive filtering and selection. + +\subsection{Graph assistant} +The graph assistant's system prompt encodes GLL's main functionalities and instructs the model to reference UI controls using predefined glyphs that are rendered as clickable action buttons in its replies, and to request a query when the user asks to filter the graph. Query generation is split into two phases. In the first, on recognizing that a query is needed, the model appends a block (``<<>>{summary, scope}<<>>'') to its reply, which is stripped from the displayed response. In the second phase, a separate structured-output call (temperature 0) sends a JSON schema to the model using Ollama's ``format'' parameter. The schema allows only the graph's existing property paths as fields and only the query language's operators. The model therefore cannot emit a non-existent property or an unsupported operator. The generated structured output is rendered to a query string that is then decoded and evaluated in the same manner as typed or GUI-built queries. If the structured output fails to render to a valid query (e.g. an empty membership list), a single repair pass retries, remapping any invalid property to its nearest valid path by Levenshtein distance. + +\subsection{Packaging and distribution} +GLL is released under the MIT license. Desktop builds for Windows, macOS, and Linux are produced with electron-builder (\url{https://www.electron.build/}) on Electron 36 (\url{https://www.electronjs.org/}). A platform-independent single-file build is produced with inline-source (\url{https://github.com/popeindustries/inline-source}), which inlines the application bundle, stylesheets, and assets into one self-contained HTML file of about 2.6 MB that runs in any modern browser. Releases are versioned semantically and distributed through GitHub Releases and GitHub Pages, with a live demo at \url{https://delta4ai.github.io/GraphLensLite/}. GLL can also run as a background service built with Node.js (\url{https://www.nodejs.org}) that exposes a token-protected REST endpoint, accepting data in GLL's JSON format and streaming it to connected browsers over Server-Sent Events, so external applications can hand off network data to GLL for interactive exploration, with concurrent users kept separate through named sessions, without writing intermediate files. The codebase is covered by over 1,300 automated tests across 66 files, written with Vitest (\url{https://vitest.dev/}). + +\section{Results} + +\begin{figure*}[!t] + \centering + \includegraphics[width=\textwidth,height=\textheight,keepaspectratio]{main-figure.png} + \caption{The graphical user interface of Graph Lens Lite. (1) Main network view, (2) File import, (3) Workspace management, (4) Data editor, (5) Query editor, (6) Network and image export, (7) Node and edge filtering, (8) Node and edge tooltips, (9) Network metrics, (10) Selection panel, (11) Style panel, (12) Edit mode, (13) Graph assistant.} + \label{fig:main-figure} +\end{figure*} + +GLL presents network exploration through a single-page interface built around a central network view and a set of dedicated panels for data input, filtering, selection, styling, metrics, data and query editors, and an assistant (Figure~\ref{fig:main-figure}). + +\subsection{Data input} +GLL accepts spreadsheets containing node and edge tables, or a JSON format preserving the application state. Input data require an ``ID'' column for nodes and ``Source ID/Target ID'' columns for edges. Optional columns may specify visual attributes such as labels, shapes, sizes, colors, or coordinates. Custom properties containing user data can be added as new columns, with optional group labels in square brackets. A template with column specifications, example values, and supported data types is available on GitHub and can also be generated from within the application. Additionally, a demo loader enables direct fetching of protein-protein interaction networks from the STRING\citep{szklarczykSTRINGDatabase20252025} database. Separately, a running GLL service instance can receive graphs programmatically from external applications over a token-protected endpoint (see packaging and distribution), without manual file handling. + +\subsection{Guided tour} +On application launch, users can start an interactive guided tour that loads a small example protein network and walks the user step-by-step through the application's core components. At each step the tour highlights relevant icons, opens panels, and explains the usage with live interface components, lowering the entry barrier for new users. + +\subsection{Graphical user interface} +The numbered elements in Figure~\ref{fig:main-figure} correspond to the following components: + +\noindent +\textbf{(1) Main network view:} The central window for displaying and interactively exploring the loaded network through panning, zooming, node dragging, hovering over elements to highlight adjacent elements, and an overview minimap. \\ +\textbf{(2) File import:} Open the launch screen to load network data from spreadsheets (node and edge tables) or portable GLL JSON files, or fetch protein-protein interaction data from the STRING database. \\ +\textbf{(3) Workspace management:} Create and switch between independent workspaces, each preserving its own layout, filters, queries, groupings, and styles. Reset the layout, fit the graph to view, hide disconnected nodes, or toggle highlight effects. \\ +\textbf{(4) Data editor:} Spreadsheet-style interactive table for adding, deleting, and editing nodes, edges, and their properties, defining new custom property columns, and export the entire network, nodes, edges, or filtered sub-networks. \\ +\textbf{(5) Query editor:} Editor for nested graph filtering and selection using a custom query language, supporting Boolean, comparison, and set-membership operators. Synchronizes with the GUI filtering panel. \\ +\textbf{(6) Network and image export:} Save the complete application state to a portable GLL JSON file for session restoration and sharing, or export the current view as high-resolution PNG. \\ +\textbf{(7) Node and edge filtering:} GUI-based selection and visual grouping, filtering via range sliders for numeric properties and category checklists for categorical values, synchronized with the query language. \\ +\textbf{(8) Node and edge tooltips:} Displays metadata for the selected node or edge element on click. \\ +\textbf{(9) Network metrics:} Compute topological centrality measures (degree, betweenness, closeness, eigenvector, and PageRank) to rank, filter, and select graph elements, with graph-level statistics and illustrative documentation for each method. \\ +\textbf{(10) Selection panel:} Shows current node and edge selection counts with lasso selection and undo/redo history. An expandable section provides additional focus, search, neighbor/edge expansion, shortest-path tracing between two selected nodes, and per-selection sub-layout controls. \\ +\textbf{(11) Style panel:} Edit node, edge, and bubble-group styles, and map data properties or computed network metrics onto visual scales (continuous and discrete color scales, numeric size scales). \\ +\textbf{(12) Edit mode:} Toggles fine-grained configuration for GUI-based filtering via precise numeric thresholds. \\ +\textbf{(13) Graph assistant:} Context-aware AI assistant that answers natural-language questions about the network, translates them into runnable queries, and summarizes the current selection. \\ + +\subsection{Features and workflow} +GLL starts by loading network data from spreadsheet files containing node and edge tables (Figure~\ref{fig:main-figure}.2) or from JSON files. User-supplied properties (coordinates, styling options) are applied where specified. Alternatively, users can fetch demo data from the STRING database. The workspace management panel (Figure~\ref{fig:main-figure}.3) allows switching between independent environments, each preserving its own aesthetics, layouts, filters, and queries. + +Once loaded, users can explore the network structure through multiple approaches. The selection panel (Figure~\ref{fig:main-figure}.10) enables element search, selection, expansion to connected neighbors, and tracing the shortest path between two selected nodes on the currently visible network. Metadata for selected node and edge elements appear in specific tooltips (Figure~\ref{fig:main-figure}.8). For deeper analysis, GLL computes five node-centrality measures (degree~\citep{freemanSetMeasuresCentrality1977a}, betweenness~\citep{freemanSetMeasuresCentrality1977a}, closeness~\citep{bavelasCommunicationPatternsTaskOriented1950a}, eigenvector~\citep{newmanMathematicsNetworks2018a} and PageRank~\citep{ brinAnatomyLargescaleHypertextual1998a}) together with graph-level statistics including network density and centralization, to identify key nodes (Figure~\ref{fig:main-figure}.9). Users can select a metric and examine one or more highly ranked nodes, with graph-level statistics displayed in the metric panel. In-app documentation describes each metric's methodology and provides references to relevant literature. The network can be arranged with a range of layout algorithms: live-animating force-directed, hierarchical, circular, radial, concentric, and circle-packing, applied either to a selection or across the entire workspace, with animated transitions between layouts and an optional overlap-removal pass. + +GLL supports network filtering through graphical controls (Figure~\ref{fig:main-figure}.7), an edit mode for precise numeric input (Figure~\ref{fig:main-figure}.12), and a query editor for complex, nested operations (Figure~\ref{fig:main-figure}.5). All interfaces are synchronized, acting on the same underlying node and edge properties. The query language is a small, purpose-built domain-specific language (DSL) rather than a free-text search. The editor validates as the user types, highlighting empty clauses, missing connectors, and unmatched brackets. Validation is grounded in the property hierarchy. Valid queries compile to an expression tree evaluated per element, so that single statements drive both filtering and selection while staying consistent with the GUI controls. Contextual help is accessible via an adjacent icon. + +GLL integrates an optional, locally hosted, context-aware AI assistant (Figure~\ref{fig:main-figure}.13), lowering the barrier to exploration by translating questions into instructions, queries and summaries. Each request embeds a snapshot of the current workspace, including node and edge counts, the property hierarchy with data types, active filters, the current query, and the currently selected nodes and edges including their property values and computed network metrics. Responses are grounded in the live graph state. When asked for a query, the assistant constructs a schema-constrained structured query representation based on the loaded graph's properties. The query is constrained to the data, referencing only real properties and supported operators. Results can be applied to select elements via rendered action buttons, or loaded into the query editor for further refinements. + +The assistant communicates with an Ollama server and is local by default, with configuration handled in the panel's settings. Enabling it requires a running Ollama installation and a downloaded instruction-following model capable of reliably creating structured JSON output. Response quality depends on the chosen model. \texttt{Qwen3.5:9b} produced strong results while running efficiently on consumer-grade graphics processing units (GPUs)\citep{teamQwen35OmniTechnicalReport2026}. Running a local endpoint prevents transmission of potentially sensitive network data to third-party services. A context-budget indicator reports how much of the selected model's context window the next request will consume and warns users when a request would exceed the threshold and be truncated. + +The styling panel (Figure~\ref{fig:main-figure}.11) offers extensive visual customization options for nodes, edges, and groups. Properties such as geometry (shape, size), color (fill, border, line), labels (text, placement, font), and annotations (badges, halos, arrows) can be configured independently for each element type. Edges can additionally render animated directional flow, with selectable flow styles (e.g. comet or chevron), adjustable opacity and density, to express directionality or magnitude. A node-density heatmap can be drawn to highlight dense regions of the layout, with controls for intensity, opacity, radius, contrast, thresholds, and predefined color maps. Up to four bubble sets allow visual grouping of nodes with adjustable appearance, populated manually or automatically through Louvain community detection. Numeric and categorical attributes, whether user-supplied or computed from network metrics, can be dynamically mapped to visual features like color and size to highlight importance. Nodes can be rendered as pie charts that encode several numeric or categorical properties as wedges, adding a data dimension beyond color and size. + +GLL includes an integrated data editor (Figure~\ref{fig:main-figure}.4) for modifying network data in tabular format. Supported operations include column sorting, cell editing, node and edge addition or deletion, table reset, and export to spreadsheet format. Users can also add new columns to define custom properties for nodes or edges, which become immediately available in the filtering and styling interfaces, enabling graph creation and manipulation entirely within the application. + +A graph can be exported in GLL's native JSON format or as a high-resolution image (Figure~\ref{fig:main-figure}.6). JSON exports preserve the complete application state, including views, filters, coordinates, and styling, enabling session restoration and collaborative sharing. + +\subsection{Modeling autosomal dominant polycystic kidney disease} +In this case study, we use GLL to visualize the molecular pathobiology of autosomal dominant polycystic kidney disease (ADPKD), two drugs, and the interference signatures that link each drug to the disease network. The corresponding datasets are provided as Excel files (\url{https://github.com/Delta4AI/GraphLensLite/tree/main/manuscript}) so readers can reproduce these views and explore the tool's features interactively. + +\begin{figure}[!t] + \centering + \includegraphics[width=\columnwidth,keepaspectratio]{adpkd-figure.png} + \caption{Visualizing disease and drug models. Panel A: Proteins of the ADPKD molecular model being associated to the Wnt signaling pathway (in green for positive modulation of the pathway, red for negative modulation of the pathway, dark blue for modulation of the pathway, and light blue for genes being pathway members without any further information on regulation) as well as additional molecules being associated with ADPKD that are linked to members of the Wnt signaling pathway (in gray) via either protein-protein interactions or co-annotations in the context of ADPKD. Panel B: The drug MoA molecular model for tolvaptan showing key associated proteins as well as associated molecular mechanisms. Panel C: The largest connected component of the ADPKD molecular model with interference signatures for tolvaptan and metformin given, indicating their complementary impact on ADPKD disease pathobiology.} + \label{fig:adpkd-model} +\end{figure} + +ADPKD is the most common inherited kidney disorder, characterized by the gradual development and enlargement of multiple cysts in both kidneys, which ultimately leads to a progressive deterioration in kidney function~\citep{formicaMolecularPathwaysInvolved2020}. ADPKD is a multi-factorial disease involving different disease mechanisms and molecular pathways. The exact mechanisms of ADPKD development and progression are still not fully understood and a better understanding of disease pathobiology is the basis for the discovery of novel drug targets and the development of new treatment options. We consolidated a set of biomedical data on ADPKD to model disease pathobiology and visualize molecular interactions using GLL. Literature associated ADPKD molecular features (genes and proteins) were extracted from Delta4's Hyper-C software platform that consists of sentence-level literature co-annotations between individual molecular features and ADPKD. This set of literature-associated ADPKD molecular features was complemented by genes showing differential expression in ADPKD as compared to healthy tissue based on Omics data. We downloaded the ADPKD bulk RNA-Seq dataset with the accession number GSE7869 from the Gene Expression Omnibus and re-analyzed the dataset to generate lists of differentially expressed genes~\citep{songSystemsBiologyAutosomal2009}. Global gene expression profiling was conducted on renal cysts of different volumes, and non-cancerous renal cortical tissue from nephrectomized kidneys was used as controls. Differential gene expression analysis involved data normalization, statistical modeling using linear models, identification of regulated genes based on $\log_2$ fold-change ($\log_2 \text{FC}$) and adjusted p-values. Ultimately, the ADPKD phenotype model was enriched with 20 genes which showed absolute $\log_2 \text{FC}$ > 2 and adjusted p-values < 0.05 across all four comparisons (large, medium, and small cysts, as well as minimally cystic tissue as compared to healthy tissue from nephrectomized kidneys respectively). In addition, we re-analyzed a single-nucleus RNA sequencing (sn-RNA-Seq) dataset with the GEO accession number GSE185948, offering insights into the cellular heterogeneity of ADPKD~\citep{mutoDefiningCellularComplexity2022}. Genes with a p-value < 0.05 were included as candidates in the analysis. Ultimately, the ADPKD phenotype molecular model was enriched with genes that were expressed in at least one cell type with absolute $\log_2 \text{FC}$ values > 2.5. In addition we retrieved information on genetic associations with ADPKD from the OpenTargets platform~\citep{bunielloOpenTargetsPlatform2025}. Expression in healthy tissue and relevant renal cell types of molecular features of the ADPKD molecular model, quantified as normalized transcripts per million (nTPMs), were extracted from the Human Protein Atlas consensus expression profile dataset\citep{karlssonSingleCellType2021}. Information on subcellular location for features of the ADPKD molecular model was retrieved from UniProt~\citep{ahmadUniProtWebsiteAPI2025}. Finally, we assigned molecular features of the ADPKD molecular model to mechanisms being discussed in recent reviews to be associated with ADPKD disease development and progression. Assignment of molecular features to mechanisms was done using the Gene Ontology (GO) annotation. + +The final constructed ADPKD molecular model consisted of 263 molecular features that were connected by 1,574 edges (\textbf{Supplementary datafile S1}). Edges in the constructed network consisted of direct protein-protein interactions consolidated from IntAct~\citep{orchardMIntActProjectIntAct2014}, BioGRID~\citep{oughtredBioGRIDDatabaseComprehensive2021}, and Reactome~\citep{milacicReactomePathwayKnowledgebase2024} as well as of literature sentence-level co-annotations of publications focusing on ADPKD using the catalogs of molecular features and phenotypes as given in Delta4's Hyper-C software platform. + +The constructed molecular model can now be used to explore the pathobiology of ADPKD by either filtering for (i) certain molecular mechanisms, (ii) individual cell types, (iii) the most up- or down-regulated genes in renal tissue or specific cell types, (iv) the most central genes based on graph properties like node degree or betweenness among others. Figure~\ref{fig:adpkd-model}A for example shows genes of the Wnt signaling pathway (in green for positive modulation of the pathway, red for negative modulation of the pathway, dark blue for modulation of the pathway, and light blue for genes being pathway members without any further information on regulation) as well as additional molecules being associated with ADPKD that are linked to members of the Wnt signaling pathway (in gray) via either protein-protein interactions or co-annotations in the context of ADPKD. This network view allows investigating novel connections of Wnt signaling pathway members with ADPKD-associated proteins. + +The molecular model can subsequently be used to (i) screen for compounds showing beneficial impact on dysregulated disease mechanisms as shown previously in other renal diseases~\citep{gebeshuberComputationalDrugRepositioning2023}, (ii) identify drug targets of interest, investigate cell-cell interaction analysis to identify relevant receptor-ligand pairs following previously reported analyses~\citep{evgeniouMetaAnalysisHumanTranscriptomics2021}, or (iii) prioritize drug targets for the development of new chemical entities. + +The only FDA-approved treatment for ADPKD is the selective vasopressin V2-receptor (AVPR2) antagonist tolvaptan. We generated a molecular mechanism of action model for tolvaptan based on literature associated tolvaptan molecular features (genes and proteins) (Figure~\ref{fig:adpkd-model}B). Molecular features were extracted from Delta4's Hyper-C software platform that consists of sentence-level literature co-annotations between individual molecular features and tolvaptan. Mechanisms were assigned to molecular features via GO annotation. The constructed MoA molecular model can subsequently be used to assess drug mechanism of action on disease pathobiology via network interference analysis with interfering nodes between the ADPKD molecular model and the tolvaptan drug MoA molecular model making up the interference signature. We can subsequently also evaluate and graphically display how two different drugs interfere with disease pathobiology on the molecular level. Figure~\ref{fig:adpkd-model}C displays the interference signature of tolvaptan (in red) and metformin (in blue), a compound currently being tested in an ongoing ADPKD clinical trial (NCT04939935) and being discussed as a compound to be combined with tolvaptan to treat patients with ADPKD~\citep{stanleySecondaryAnalysisConcurrent2024}. Network visualization suggests a complementary impact of the two drugs on ADPKD disease pathobiology due to the non-overlapping interfering signatures between ADPKD and the two drug models. + +% ─────────────────────────────────────────────────────────────────────────── +\section{Discussion} +We have developed GLL, an interactive, browser-based network viewer for exploring and sharing biological networks through a streamlined interface. It combines an expressive query language with topological network analysis, interactive filtering, visual grouping, customizable layouts, a data editor, fine-grained property-based styling, animated edge-flow visualization, community detection, and a context-aware, locally hosted AI assistant, supporting the visual exploration of molecular models of disease pathobiology or drug mechanism of action. GLL requires only a web browser for core functionality: it is distributed as a single self-contained HTML file, which can be combined with a network model into a compact archive of a few megabytes for sharing, while platform-specific Electron bundles provide native desktop applications across Windows, macOS, and Linux. This minimal footprint enables collaboration, long-term stability, and offline operation. + +Table~\ref{tab:tools} compares GLL against biomedical network exploration tools across their deployment surface (web, desktop, or single-file HTML), offline usability versus server dependency, portability of the exploration state, editability of the underlying data, the presence of a query language for complex filtering, and the availability of an AI assistant. + +\begin{table}[!htbp] +\centering +\caption{Comparison of selected network analysis and visualization tools for biomedical research. \emph{Single-file HTML}: the entire application is one HTML file that runs locally with no install procedure or server. \emph{Offline-ready}: core functionality works without an external server or third-party API (optional demo data loaders may still require network access). \emph{Portable state}: the complete application state (data, view, filters, layout, and styling) exports and re-imports as a single file. \emph{Data editor}: nodes, edges, and custom properties can be created, edited, and deleted from the GUI. \emph{Query DSL}: a domain-specific query language for complex graph filtering is available. \emph{AI assistant}: built-in assistant powered by a locally hosted open-source model for natural-language interaction. Cells: \checkmark{} present, -- absent.} +\label{tab:tools} +\footnotesize +\setlength{\tabcolsep}{3pt} +\renewcommand{\arraystretch}{1.1} +\newcommand{\rh}[1]{\rotatebox[origin=l]{90}{#1}} +\begin{tabularx}{\columnwidth}{@{}X *{8}{>{\centering\arraybackslash}p{0.55cm}}@{}} +\toprule +Tool & \rh{Web} & \rh{Desktop} & \rh{Single-file HTML} & \rh{Offline-ready} & \rh{Portable state} & \rh{Data editor} & \rh{Query DSL} & \rh{AI assistant} \\ +\midrule +Graph Lens Lite & \checkmark & \checkmark & \checkmark & \checkmark & \checkmark & \checkmark & \checkmark & \checkmark \\ +Cytoscape~\citep{shannonCytoscapeSoftwareEnvironment2003} & -- & \checkmark & -- & \checkmark & \checkmark & \checkmark & \checkmark\textsuperscript{a} & -- \\ +Cytoscape Web~\citep{onoCytoscapeWebBringing2025} & \checkmark & -- & -- & -- & \checkmark & \checkmark & -- & \checkmark\textsuperscript{b} \\ +Gephi~\citep{bastianGephiOpenSource2009} & -- & \checkmark & -- & \checkmark & \checkmark & \checkmark & -- & -- \\ +Gephi Lite~\citep{jacomyGephiGephilite2025} & \checkmark & -- & -- & \checkmark & \checkmark & \checkmark & -- & -- \\ +Graphia~\citep{freemanGraphiaPlatformGraphbased2022} & \checkmark & \checkmark & -- & \checkmark & \checkmark & \checkmark & \checkmark\textsuperscript{c} & -- \\ +OmicsNet 2.0~\citep{zhouOmicsNet20Webbased2022} & \checkmark & -- & -- & -- & \checkmark\textsuperscript{d} & -- & -- & -- \\ +STRING~\citep{szklarczykSTRINGDatabase20252025} & \checkmark\textsuperscript{e} & -- & -- & -- & \checkmark & -- & -- & -- \\ +Reactome~\citep{milacicReactomePathwayKnowledgebase2024} & \checkmark\textsuperscript{e} & -- & -- & -- & \checkmark & -- & -- & -- \\ + \bottomrule +\addlinespace[2pt] +\multicolumn{9}{@{}p{\columnwidth}@{}}{\scriptsize + \textsuperscript{a}Cytoscape offers Lucene full-text search and a GUI filter builder, but no unified query language.\quad + \textsuperscript{b}Cytoscape Web supports OpenAI via API key, but no local model.\quad + \textsuperscript{c}Graphia's transform GUI is backed by a DSL internally but is not user-queryable.\quad + \textsuperscript{d}OmicsNet 2.0 offers JSON export, but re-import was unreliable in our testing.\quad + \textsuperscript{e}STRING and Reactome Pathway Browser encode a shareable state in a URL, not a full workspace.\quad} +\end{tabularx} +\end{table} + +While Table~\ref{tab:tools} provides a direct comparison across tools, some entries require further explanation to highlight GLL's distinct capabilities. STRING Search\citep{szklarczykSTRINGDatabase20252025} (\url{https://string-db.org/cgi/input}) and the Reactome Pathway Browser\citep{milacicReactomePathwayKnowledgebase2024} (\url{https://curator.reactome.org/PathwayBrowser}) provide shareable states by encoding the current selection and analysis parameters into a URL, rather than serializing the full workspace. OmicsNet 2.0\citep{zhouOmicsNet20Webbased2022} offers JSON export whose re-import proved unreliable in our testing, reloading as a differently styled network including more than the exported set of nodes. To elaborate on domain specific query languages integrated into the application, Graphia's\citep{freemanGraphiaPlatformGraphbased2022} transform GUI is backed by a DSL internally but does not expose it for users to write or share, while Cytoscape\citep{shannonCytoscapeSoftwareEnvironment2003} combines Lucene-style full-text search with a visual GUI-based filter engine, rather than a single, shareable query surface. GLL couples one validated query language with its GUI controls, so the same expression can be written by hand, constructed through the interface, or generated by the assistant. Of the web-based tools compared, only Cytoscape Web\citep{onoCytoscapeWebBringing2025} provides a comparable assistant, but it routes requests to OpenAI (\url{https://openai.com/}) through a user-supplied API key with no local-model path, whereas GLL's assistant runs against a locally hosted model so that graph data never leaves the machine. GLL therefore positions itself in a niche rather than replacing established platforms: it does not match the plugin ecosystems of Cytoscape and Gephi\citep{bastianGephiOpenSource2009} or the database breadth of STRING, OmicsNet, and Reactome, but complements them with portable, offline-capable, and highly customizable exploration of focused, curated networks. Unlike canvas-based web renderers, GLL uses a GPU-accelerated WebGL engine, sustaining interactive exploration of large networks while retaining full styling, filtering, and grouping capabilities. + +\subsection{Limitations} +GLL targets curated, interpretable networks rather than genome-scale topologies. WebGL rendering keeps panning, zooming, and selection interactive into the tens of thousands of elements, though larger networks combined with heavy per-element styling may still reduce responsiveness. + +The network metric analysis covers five centrality measures computed on undirected graph data, and community structure can be revealed through Louvain community detection mapped onto visual groups. Directed-graph metrics are not yet supported. Path tracing between selected features is supported as an unweighted shortest path computed on the visible (filtered) subgraph. Weighted path costs are not considered, and only a single shortest path is returned when several of equal length exist. + +While the current export/import flow reliably handles GLL's JSON format, support for standardized network formats such as GraphML or CX2, and interoperability with established tools such as Cytoscape or Gephi could benefit applied biomedical researchers. + +Keeping GLL's query language and GUI synchronized proved difficult, as nested statements and negations do not map cleanly to graphical controls given the current design. The filtering panel therefore offers a single join mode applied across all clauses — OR by default or AND, the latter with an optional complete-cases mode — but cannot express the arbitrary nesting or negation supported by the raw query language. When a raw query is modified and applied to filter the graph, the GUI filtering panel is locked and flagged with a note that the query takes precedence, with the option to unlock it by resetting the query to match the GUI's state. Value changes, property selections, and the join mode remain synchronized as the query is edited, whereas nesting and negation do not. + +\section{Acknowledgements} +The authors want to thank all working group members of the EU COST Action Program PerMediK (Personalized Medicine in Chronic Kidney Disease: Improved outcome based on Big Data) for fruitful discussions during the regular COST Action project meetings. Paul Perco and Matthias Ley are members of the EU COST Action Program PerMediK, CA21165. + +\textit{Author contributions:} M.L.: conceptualization; resources; software; visualization; writing - original draft, review \& editing. K.K.-I.: data curation; validation; writing - review \& editing. L.F.: validation; writing - review \& editing. S.W.: validation; writing - review \& editing. F.B.: validation; writing - review \& editing. E.B.: validation; writing - review \& editing. L.G.: validation; writing - review \& editing. P.A.: validation; writing - review \& editing. P.H.: data curation; validation; writing - review \& editing. J.L.: data curation; funding acquisition; validation; writing - review \& editing. P.P.: conceptualization; data curation; funding acquisition; supervision; validation; writing - original draft, review \& editing. K.K.: funding acquisition; supervision; validation; writing - review \& editing. + +\section{Supplementary Data} +Supplementary data is available at NAR online. + +\section{Conflict of interest} +K.K. is a co-founder of Delta4 GmbH (Vienna, Austria). M.L., K.K.-I., L.F., S.W., F.B., E.B., L.G., P.A., and P.P. are employed at Delta4 GmbH (Vienna, Austria). + +\section{Funding} +This project has received funding from the Austrian Research Promotion Agency (FFG) under grant agreement number 915133 (ADPKD Drug Discovery). Enrico Bono is supported by a grant from the European Union's Horizon Europe Marie Skłodowska-Curie Actions Doctoral Networks program project PICKED (HORIZON-MSCA-2023-DN-01, grant number 101168626). Louiza Galou is supported by a grant from the European Union's Horizon Europe Marie Skłodowska-Curie Actions Doctoral Networks Industrial Doctorates program project PROMOTE (HORIZON-MSCA-2023-DN-01, grant number 101169245). This work was further supported by COST Action CA21165 (PerMediK), funded by COST (European Cooperation in Science and Technology). + +\section{Data availability} +The application source code, distribution bundles, templates, the curated ADPKD molecular model, the drug mechanism of action model for tolvaptan, and the interference model are available at GitHub (\url{https://github.com/Delta4AI/GraphLensLite}) and Zenodo (\url{https://doi.org/10.5281/zenodo.21035263}). This website is free and open to all users and there is no login requirement. diff --git a/manuscript/graphical-abstract.html b/manuscript/graphical-abstract.html new file mode 100644 index 0000000..48975ca --- /dev/null +++ b/manuscript/graphical-abstract.html @@ -0,0 +1,226 @@ + + + + +Graph Lens Lite — Graphical Abstract v3 + + + +
+ + +
+ +
+
Graph Lens Lite
+
Interactive biological network exploration in the browser
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Query & Filtering
+
Network Metrics
+
Layouts
+
+ + +
+
Rich Styling and Visual Clustering
+
Local AI Graph Assistant
+
Data Editor
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + PKD1 + PKD2 + PKHD1 + + + +
+
+
+ + +
+
Import
+
XLSX Excel
+
TSV CSV / TSV
+
Server App handoff
+
API REST
+
+ + +
+
Export
+
XLSX Excel
+
PNG Raster image
+
SVG Vector image
+
JSON Portable state
+
+ +
+ + diff --git a/manuscript/keywords.tex b/manuscript/keywords.tex new file mode 100644 index 0000000..d15f6e4 --- /dev/null +++ b/manuscript/keywords.tex @@ -0,0 +1 @@ +biological network visualization, interactive graph exploration, web-based application, systems biology, open-source software \ No newline at end of file diff --git a/manuscript/main.tex b/manuscript/main.tex new file mode 100644 index 0000000..0be80ed --- /dev/null +++ b/manuscript/main.tex @@ -0,0 +1,81 @@ +%% Template-specific Frontmatter +%% +%% Requires the OUP template bundle (ships in recent TeX Live versions) (oup-authoring-template) +%% Alternatively, copy oup-authoring-template.cls and the oup-*.bst files from the OUP template +%% (https://www.overleaf.com/latex/templates/oup-general-template/ybpypwncdxyb) +%% into this directory. +%% +%% Document bodies: +%% body.tex +%% abstract.tex +%% keywords.tex + +\documentclass[unnumsec,webpdf,contemporary,large]{oup-authoring-template} + +\usepackage[T1]{fontenc} % special characters (ȩ, ß) +\usepackage{amsmath,amssymb} +\usepackage{booktabs} +\usepackage{tabularx} +\usepackage{url} + +% Numbered citations in round parentheses, in order of appearance +\setcitestyle{numbers,round,comma} + +\graphicspath{{Fig/}} + +% Line numbers for peer review +\usepackage[mathlines, switch]{lineno} +\linenumbers + +\begin{document} + +\journaltitle{Nucleic Acids Research} +\DOI{DOI added during production} +\copyrightyear{2026} +\pubyear{2026} +\vol{00} +\issue{0} +\access{Advance Access Publication Date: Day Month Year} +\appnotes{Paper} + +\firstpage{1} + +\title[Graph Lens Lite]{Graph Lens Lite: A browser-based tool for interactive visualization and exploration of biological networks} + +\author[1,2,$\ast$]{Matthias Ley\ORCID{0000-0001-6853-8542}} +\author[1]{Kinga K\k{e}ska-Izworska\ORCID{0000-0002-4329-1499}} +\author[1]{Lucas Fillinger\ORCID{0000-0001-5341-8097}} +\author[1]{Samuel M Walter\ORCID{0000-0002-6868-5654}} +\author[1]{Fabio Baumg\"{a}rtel\ORCID{0009-0000-0243-2673}} +\author[1,2]{Enrico Bono\ORCID{0009-0007-7272-5034}} +\author[1,2]{Louiza Galou\ORCID{0009-0008-5176-2802}} +\author[1]{Peter Andorfer\ORCID{0009-0003-3492-4017}} +\author[3]{Pirmin Hauser} +\author[3]{Johannes Leierer\ORCID{0000-0002-7884-9827}} +\author[1,2,$\dagger$,$\ast\ast$]{Klaus Kratochwill\ORCID{0000-0003-0803-614X}} +\author[1,3,$\dagger$]{Paul Perco\ORCID{0000-0003-2087-5691}} + +\address[1]{\orgname{Delta4 GmbH}, \orgaddress{\street{Alser Stra\ss{}e 23/30}, \postcode{1080}, \state{Vienna}, \country{Austria}}} +\address[2]{\orgdiv{Division of Pediatric Nephrology and Gastroenterology, Department of Pediatrics and Adolescent Medicine, Comprehensive Center for Pediatrics}, \orgname{Medical University Vienna}, \orgaddress{\street{Waehringer Guertel 18-20}, \postcode{1090}, \state{Vienna}, \country{Austria}}} +\address[3]{\orgdiv{Department of Internal Medicine IV}, \orgname{Medical University Innsbruck}, \orgaddress{\street{Anichstrasse 35}, \postcode{6020}, \state{Innsbruck}, \country{Austria}}} + +\corresp[$\ast$]{Corresponding author. \href{mailto:matthias.ley@delta4.ai}{matthias.ley@delta4.ai}\\} +\corresp[$\ast\ast$]{Correspondence may also be addressed to \href{mailto:klaus.kratochwill@delta4.ai}{klaus.kratochwill@delta4.ai}\\} +\corresp[$\dagger$]{These authors contributed equally.} + +\received{Date}{0}{Year} +\revised{Date}{0}{Year} +\accepted{Date}{0}{Year} + +\abstract{\input{abstract}} + +\keywords{\input{keywords}} + +\maketitle + +\input{body} + +\bibliographystyle{oup-plain} +\bibliography{reference} + +\end{document} diff --git a/manuscript/oxford/oup-plain.bst b/manuscript/oup-plain.bst similarity index 98% rename from manuscript/oxford/oup-plain.bst rename to manuscript/oup-plain.bst index 5190c94..f6a3305 100644 --- a/manuscript/oxford/oup-plain.bst +++ b/manuscript/oup-plain.bst @@ -31,6 +31,7 @@ ENTRY type volume year + doi } {} { label } @@ -92,9 +93,20 @@ FUNCTION {output.bibitem} before.all 'output.state := } +FUNCTION {format.doi} +{ doi empty$ + { "" } + { "\url{https://doi.org/" doi * "}" * } + if$ +} + FUNCTION {fin.entry} { add.period$ write$ + format.doi duplicate$ empty$ + 'pop$ + { " " swap$ * write$ } + if$ newline$ } @@ -1046,9 +1058,9 @@ FUNCTION {presort} 'sort.key$ := } -ITERATE {presort} - -SORT +% NAR: references numbered in order of appearance, not sorted. +% ITERATE {presort} +% SORT STRINGS { longest.label } diff --git a/manuscript/oxford/main.tex b/manuscript/oxford/main.tex deleted file mode 100644 index df15dc5..0000000 --- a/manuscript/oxford/main.tex +++ /dev/null @@ -1,91 +0,0 @@ -%% -%% Oxford University Press — Bioinformatics -%% -\documentclass[numsec,webpdf,modern,large,namedate]{oup-authoring-template} - -% ── Venue flag ──────────────────────────────────────────────────────────────── -\newif\ifbiorxiv -\biorxivfalse - -% ── Packages ────────────────────────────────────────────────────────────────── -\usepackage[T1]{fontenc} -\graphicspath{{../shared/Fig/}} - -% line numbers (uncomment for review) -%\usepackage[mathlines, switch]{lineno} -%\linenumbers - -% ── Theorem styles (required by cls) ───────────────────────────────────────── -\theoremstyle{thmstyleone} -\newtheorem{theorem}{Theorem} -\newtheorem{proposition}[theorem]{Proposition} -\theoremstyle{thmstyletwo} -\newtheorem{example}{Example} -\newtheorem{remark}{Remark} -\theoremstyle{thmstylethree} -\newtheorem{definition}{Definition} - -\providecommand{\authormark}[1]{\gdef\@authormark{#1}} -\begin{document} - -% ── Journal metadata ───────────────────────────────────────────────────────── -\journaltitle{Bioinformatics} -\DOI{10.1093/bioinformatics/xxxxx} -\copyrightyear{2026} -\pubyear{2026} -\access{Advance Access Publication Date: Day Month Year} -\appnotes{Applications Note} -\firstpage{1} - -% ── Title ──────────────────────────────────────────────────────────────────── -\title{Graph Lens Lite: An interactive biological network viewer for displaying, exploring, and sharing disease pathobiology and drug mechanism of action models} - -% ── Authors & affiliations ─────────────────────────────────────────────────── -\author[1,2,$\ast$]{Matthias Ley\ORCID{0000-0001-6853-8542}} -\author[1]{Kinga Kęska-Izworska\ORCID{0000-0002-4329-1499}} -\author[1]{Lucas Fillinger\ORCID{0000-0001-5341-8097}} -\author[1]{Samuel M Walter\ORCID{0000-0002-6868-5654}} -\author[1]{Fabio Baumg\"{a}rtel\ORCID{0009-0000-0243-2673}} -\author[1,2]{Enrico Bono\ORCID{0009-0007-7272-5034}} -\author[1,2]{Louiza Galou\ORCID{0009-0008-5176-2802}} -\author[1]{Peter Andorfer\ORCID{0009-0003-3492-4017}} -\author[3]{Pirmin Hauser} -\author[3]{Johannes Leierer\ORCID{0000-0002-7884-9827}} -\author[1,2,$\#$,$\ast$]{Klaus Kratochwill\ORCID{0000-0003-0803-614X}} -\author[1,3,$\#$]{Paul Perco\ORCID{0000-0003-2087-5691}} - -\authormark{Ley et al.} - -\address[1]{\orgname{Delta4 GmbH}, \orgaddress{\street{Alser Stra\ss{}e 23/30}, \postcode{1080}, \state{Vienna}, \country{Austria}}} -\address[2]{\orgdiv{Division of Pediatric Nephrology and Gastroenterology, Department of Pediatrics and Adolescent Medicine, Comprehensive Center for Pediatrics}, \orgname{Medical University Vienna}, \orgaddress{\street{Waehringer Guertel 18-20}, \postcode{1090}, \state{Vienna}, \country{Austria}}} -\address[3]{\orgdiv{Department of Internal Medicine IV}, \orgname{Medical University Innsbruck}, \orgaddress{\street{Anichstrasse 35}, \postcode{6020}, \state{Innsbruck}, \country{Austria}}} - -\corresp[$\ast$]{Corresponding authors: \href{mailto:matthias.ley@delta4.ai}{matthias.ley@delta4.ai}, \href{mailto:klaus.kratochwill@delta4.ai}{klaus.kratochwill@delta4.ai}\\} - -\corresp[$\#$]{Contributed equally} - -\received{Date}{0}{Year} -\revised{Date}{0}{Year} -\accepted{Date}{0}{Year} - -% ── Abstract ───────────────────────────────────────────────────────────────── -\abstract{\mdseries -\textbf{Motivation:} Biological network visualization together with graph-based analyses are key techniques in systems biology and network medicine to detect patterns and generate new hypotheses regarding disease pathobiology, drug target identification, biomarker prioritization, or digital drug discovery. Network representations are also a way to communicate research findings and share results with colleagues and coworkers. \\ -\textbf{Results:} We have developed Graph Lens Lite, a browser-based tool that combines rich visualization capabilities with a streamlined interface for exploring and sharing biological networks. It offers an expressive query language, topological network analysis, GUI-based filtering, visual grouping, customizable layouts, a data-editor, and fine-grained property-based styling options, particularly suited for visualizing molecular models of disease pathobiology or drug mechanism of action. \\ -\textbf{Availability:} Graph Lens Lite is available at GitHub (\url{https://github.com/Delta4AI/GraphLensLite}).\\ -\textbf{Contact:} \href{mailto:matthias.ley@delta4.ai}{matthias.ley@delta4.ai}\\ -\textbf{Supplementary information:} Supplementary data are available at \textit{Bioinformatics} online. -} - -\keywords{network visualization, graph exploration, disease pathobiology, drug mechanism of action} - -\maketitle - -% ── Shared body ────────────────────────────────────────────────────────────── -\input{../shared/content} - -% ── Bibliography ───────────────────────────────────────────────────────────── -\bibliographystyle{oup-abbrvnat} -\bibliography{../shared/reference} - -\end{document} diff --git a/manuscript/oxford/oup-abbrvnat.bst b/manuscript/oxford/oup-abbrvnat.bst deleted file mode 100644 index bab76f7..0000000 --- a/manuscript/oxford/oup-abbrvnat.bst +++ /dev/null @@ -1,1452 +0,0 @@ -%% File: `oup-abbrvnat.bst' -%% A modification of `abbrv.bst' for use with natbib package -%% -%% Copyright 1993-2007 Patrick W Daly -%% Max-Planck-Institut f\"ur Sonnensystemforschung -%% Max-Planck-Str. 2 -%% D-37191 Katlenburg-Lindau -%% Germany -%% E-mail: daly@mps.mpg.de -%% -%% This program can be redistributed and/or modified under the terms -%% of the LaTeX Project Public License Distributed from CTAN -%% archives in directory macros/latex/base/lppl.txt; either -%% version 1 of the License, or any later version. -%% - % Version and source file information: - % \ProvidesFile{natbst.mbs}[2007/11/26 1.93 (PWD)] - % - % BibTeX `plainnat' family - % version 0.99b for BibTeX versions 0.99a or later, - % for LaTeX versions 2.09 and 2e. - % - % For use with the `natbib.sty' package; emulates the corresponding - % member of the `plain' family, but with author-year citations. - % - % With version 6.0 of `natbib.sty', it may also be used for numerical - % citations, while retaining the commands \citeauthor, \citefullauthor, - % and \citeyear to print the corresponding information. - % - % For version 7.0 of `natbib.sty', the KEY field replaces missing - % authors/editors, and the date is left blank in \bibitem. - % - % Includes field EID for the sequence/citation number of electronic journals - % which is used instead of page numbers. - % - % Includes fields ISBN and ISSN. - % - % Includes field URL for Internet addresses. - % - % Includes field DOI for Digital Object Idenfifiers. - % - % Works best with the url.sty package of Donald Arseneau. - % - % Works with identical authors and year are further sorted by - % citation key, to preserve any natural sequence. - % -ENTRY - { address - author - booktitle - chapter - doi - eid - edition - editor - howpublished - institution - isbn - issn - journal - key - month - note - number - organization - pages - publisher - school - series - title - type - url - volume - year - } - {} - { label extra.label sort.label short.list } - -INTEGERS { output.state before.all mid.sentence after.sentence after.block } - -FUNCTION {init.state.consts} -{ #0 'before.all := - #1 'mid.sentence := - #2 'after.sentence := - #3 'after.block := -} - -STRINGS { s t } - -FUNCTION {output.nonnull} -{ 's := - output.state mid.sentence = - { ", " * write$ } - { output.state after.block = - { add.period$ write$ - newline$ - "\newblock " write$ - } - { output.state before.all = - 'write$ - { add.period$ " " * write$ } - if$ - } - if$ - mid.sentence 'output.state := - } - if$ - s -} - -FUNCTION {output} -{ duplicate$ empty$ - 'pop$ - 'output.nonnull - if$ -} - -FUNCTION {output.check} -{ 't := - duplicate$ empty$ - { pop$ "empty " t * " in " * cite$ * warning$ } - 'output.nonnull - if$ -} - -FUNCTION {fin.entry} -{ add.period$ - write$ - newline$ -} - -FUNCTION {new.block} -{ output.state before.all = - 'skip$ - { after.block 'output.state := } - if$ -} - -FUNCTION {new.sentence} -{ output.state after.block = - 'skip$ - { output.state before.all = - 'skip$ - { after.sentence 'output.state := } - if$ - } - if$ -} - -FUNCTION {not} -{ { #0 } - { #1 } - if$ -} - -FUNCTION {and} -{ 'skip$ - { pop$ #0 } - if$ -} - -FUNCTION {or} -{ { pop$ #1 } - 'skip$ - if$ -} - -FUNCTION {new.block.checka} -{ empty$ - 'skip$ - 'new.block - if$ -} - -FUNCTION {new.block.checkb} -{ empty$ - swap$ empty$ - and - 'skip$ - 'new.block - if$ -} - -FUNCTION {new.sentence.checka} -{ empty$ - 'skip$ - 'new.sentence - if$ -} - -FUNCTION {new.sentence.checkb} -{ empty$ - swap$ empty$ - and - 'skip$ - 'new.sentence - if$ -} - -FUNCTION {field.or.null} -{ duplicate$ empty$ - { pop$ "" } - 'skip$ - if$ -} - -FUNCTION {emphasize} -{ duplicate$ empty$ - { pop$ "" } - { "\emph{" swap$ * "}" * } - if$ -} - -INTEGERS { nameptr namesleft numnames } - -FUNCTION {format.names} -{ 's := - #1 'nameptr := - s num.names$ 'numnames := - numnames #3 > - { #3 'namesleft := - { namesleft #0 > } - { s nameptr "{f.~}{vv~}{ll}{, jj}" format.name$ 't := - nameptr #1 > - { ", " * t * } - 't - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ - " et~al." * - } - { numnames 'namesleft := - { namesleft #0 > } - { s nameptr "{f.~}{vv~}{ll}{, jj}" format.name$ 't := - nameptr #1 > - { namesleft #1 > - { ", " * t * } - { numnames #2 > - { "," * } - 'skip$ - if$ - t "others" = - { " et~al." * } - { " and " * t * } - if$ - } - if$ - } - 't - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ - } - if$ -} - -FUNCTION {format.key} -{ empty$ - { key field.or.null } - { "" } - if$ -} - -FUNCTION {format.authors} -{ author empty$ - { "" } - { author format.names } - if$ -} - -FUNCTION {format.editors} -{ editor empty$ - { "" } - { editor format.names - editor num.names$ #1 > - { ", editors" * } - { ", editor" * } - if$ - } - if$ -} - -FUNCTION {format.isbn} -{ isbn empty$ - { "" } - { new.block "ISBN " isbn * } - if$ -} - -FUNCTION {format.issn} -{ issn empty$ - { "" } - { new.block "ISSN " issn * } - if$ -} - -FUNCTION {format.url} -{ url empty$ - { "" } - { new.block "URL \url{" url * "}" * } - if$ -} - -FUNCTION {format.doi} -{ doi empty$ - { "" } - { new.block "\doi{" doi * "}" * } - if$ -} - -FUNCTION {format.title} -{ title empty$ - { "" } - { title "t" change.case$ } - if$ -} - -FUNCTION {format.full.names} -{'s := - #1 'nameptr := - s num.names$ 'numnames := - numnames 'namesleft := - { namesleft #0 > } - { s nameptr - "{vv~}{ll}" format.name$ 't := - nameptr #1 > - { - namesleft #1 > - { ", " * t * } - { - numnames #2 > - { "," * } - 'skip$ - if$ - t "others" = - { " et~al." * } - { " and " * t * } - if$ - } - if$ - } - 't - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ -} - -FUNCTION {author.editor.full} -{ author empty$ - { editor empty$ - { "" } - { editor format.full.names } - if$ - } - { author format.full.names } - if$ -} - -FUNCTION {author.full} -{ author empty$ - { "" } - { author format.full.names } - if$ -} - -FUNCTION {editor.full} -{ editor empty$ - { "" } - { editor format.full.names } - if$ -} - -FUNCTION {make.full.names} -{ type$ "book" = - type$ "inbook" = - or - 'author.editor.full - { type$ "proceedings" = - 'editor.full - 'author.full - if$ - } - if$ -} - -FUNCTION {output.bibitem} -{ newline$ - "\bibitem[" write$ - label write$ - ")" make.full.names duplicate$ short.list = - { pop$ } - { * } - if$ - "]{" * write$ - cite$ write$ - "}" write$ - newline$ - "" - before.all 'output.state := -} - -FUNCTION {n.dashify} -{ 't := - "" - { t empty$ not } - { t #1 #1 substring$ "-" = - { t #1 #2 substring$ "--" = not - { "--" * - t #2 global.max$ substring$ 't := - } - { { t #1 #1 substring$ "-" = } - { "-" * - t #2 global.max$ substring$ 't := - } - while$ - } - if$ - } - { t #1 #1 substring$ * - t #2 global.max$ substring$ 't := - } - if$ - } - while$ -} - -FUNCTION {format.date} -{ year duplicate$ empty$ - { "empty year in " cite$ * warning$ - pop$ "" } - 'skip$ - if$ - month empty$ - 'skip$ - { month - " " * swap$ * - } - if$ - extra.label * -} - -FUNCTION {format.btitle} -{ title emphasize -} - -FUNCTION {tie.or.space.connect} -{ duplicate$ text.length$ #3 < - { "~" } - { " " } - if$ - swap$ * * -} - -FUNCTION {either.or.check} -{ empty$ - 'pop$ - { "can't use both " swap$ * " fields in " * cite$ * warning$ } - if$ -} - -FUNCTION {format.bvolume} -{ volume empty$ - { "" } - { "volume" volume tie.or.space.connect - series empty$ - 'skip$ - { " of " * series emphasize * } - if$ - "volume and number" number either.or.check - } - if$ -} - -FUNCTION {format.number.series} -{ volume empty$ - { number empty$ - { series field.or.null } - { output.state mid.sentence = - { "number" } - { "Number" } - if$ - number tie.or.space.connect - series empty$ - { "there's a number but no series in " cite$ * warning$ } - { " in " * series * } - if$ - } - if$ - } - { "" } - if$ -} - -FUNCTION {format.edition} -{ edition empty$ - { "" } - { output.state mid.sentence = - { edition "l" change.case$ " edition" * } - { edition "t" change.case$ " edition" * } - if$ - } - if$ -} - -INTEGERS { multiresult } - -FUNCTION {multi.page.check} -{ 't := - #0 'multiresult := - { multiresult not - t empty$ not - and - } - { t #1 #1 substring$ - duplicate$ "-" = - swap$ duplicate$ "," = - swap$ "+" = - or or - { #1 'multiresult := } - { t #2 global.max$ substring$ 't := } - if$ - } - while$ - multiresult -} - -FUNCTION {format.pages} -{ pages empty$ - { "" } - { pages multi.page.check - { "pages" pages n.dashify tie.or.space.connect } - { "page" pages tie.or.space.connect } - if$ - } - if$ -} - -FUNCTION {format.eid} -{ eid empty$ - { "" } - { "art." eid tie.or.space.connect } - if$ -} - -FUNCTION {format.vol.num.pages} -{ volume field.or.null - number empty$ - 'skip$ - { "\penalty0 (" number * ")" * * - volume empty$ - { "there's a number but no volume in " cite$ * warning$ } - 'skip$ - if$ - } - if$ - pages empty$ - 'skip$ - { duplicate$ empty$ - { pop$ format.pages } - { ":\penalty0 " * pages n.dashify * } - if$ - } - if$ -} - -FUNCTION {format.vol.num.eid} -{ volume field.or.null - number empty$ - 'skip$ - { "\penalty0 (" number * ")" * * - volume empty$ - { "there's a number but no volume in " cite$ * warning$ } - 'skip$ - if$ - } - if$ - eid empty$ - 'skip$ - { duplicate$ empty$ - { pop$ format.eid } - { ":\penalty0 " * eid * } - if$ - } - if$ -} - -FUNCTION {format.chapter.pages} -{ chapter empty$ - 'format.pages - { type empty$ - { "chapter" } - { type "l" change.case$ } - if$ - chapter tie.or.space.connect - pages empty$ - 'skip$ - { ", " * format.pages * } - if$ - } - if$ -} - -FUNCTION {format.in.ed.booktitle} -{ booktitle empty$ - { "" } - { editor empty$ - { "In " booktitle emphasize * } - { "In " format.editors * ", " * booktitle emphasize * } - if$ - } - if$ -} - -FUNCTION {empty.misc.check} -{ author empty$ title empty$ howpublished empty$ - month empty$ year empty$ note empty$ - and and and and and - key empty$ not and - { "all relevant fields are empty in " cite$ * warning$ } - 'skip$ - if$ -} - -FUNCTION {format.thesis.type} -{ type empty$ - 'skip$ - { pop$ - type "t" change.case$ - } - if$ -} - -FUNCTION {format.tr.number} -{ type empty$ - { "Technical Report" } - 'type - if$ - number empty$ - { "t" change.case$ } - { number tie.or.space.connect } - if$ -} - -FUNCTION {format.article.crossref} -{ key empty$ - { journal empty$ - { "need key or journal for " cite$ * " to crossref " * crossref * - warning$ - "" - } - { "In \emph{" journal * "}" * } - if$ - } - { "In " } - if$ - " \citet{" * crossref * "}" * -} - -FUNCTION {format.book.crossref} -{ volume empty$ - { "empty volume in " cite$ * "'s crossref of " * crossref * warning$ - "In " - } - { "Volume" volume tie.or.space.connect - " of " * - } - if$ - editor empty$ - editor field.or.null author field.or.null = - or - { key empty$ - { series empty$ - { "need editor, key, or series for " cite$ * " to crossref " * - crossref * warning$ - "" * - } - { "\emph{" * series * "}" * } - if$ - } - 'skip$ - if$ - } - 'skip$ - if$ - " \citet{" * crossref * "}" * -} - -FUNCTION {format.incoll.inproc.crossref} -{ editor empty$ - editor field.or.null author field.or.null = - or - { key empty$ - { booktitle empty$ - { "need editor, key, or booktitle for " cite$ * " to crossref " * - crossref * warning$ - "" - } - { "In \emph{" booktitle * "}" * } - if$ - } - { "In " } - if$ - } - { "In " } - if$ - " \citet{" * crossref * "}" * -} - -FUNCTION {article} -{ output.bibitem - format.authors "author" output.check - author format.key output - new.block - format.title "title" output.check - new.block - crossref missing$ - { journal emphasize "journal" output.check - eid empty$ - { format.vol.num.pages output } - { format.vol.num.eid output } - if$ - format.date "year" output.check - } - { format.article.crossref output.nonnull - eid empty$ - { format.pages output } - { format.eid output } - if$ - } - if$ - format.issn output - format.doi output - format.url output - new.block - note output - fin.entry -} - -FUNCTION {book} -{ output.bibitem - author empty$ - { format.editors "author and editor" output.check - editor format.key output - } - { format.authors output.nonnull - crossref missing$ - { "author and editor" editor either.or.check } - 'skip$ - if$ - } - if$ - new.block - format.btitle "title" output.check - crossref missing$ - { format.bvolume output - new.block - format.number.series output - new.sentence - publisher "publisher" output.check - address output - } - { new.block - format.book.crossref output.nonnull - } - if$ - format.edition output - format.date "year" output.check - format.isbn output - format.doi output - format.url output - new.block - note output - fin.entry -} - -FUNCTION {booklet} -{ output.bibitem - format.authors output - author format.key output - new.block - format.title "title" output.check - howpublished address new.block.checkb - howpublished output - address output - format.date output - format.isbn output - format.doi output - format.url output - new.block - note output - fin.entry -} - -FUNCTION {inbook} -{ output.bibitem - author empty$ - { format.editors "author and editor" output.check - editor format.key output - } - { format.authors output.nonnull - crossref missing$ - { "author and editor" editor either.or.check } - 'skip$ - if$ - } - if$ - new.block - format.btitle "title" output.check - crossref missing$ - { format.bvolume output - format.chapter.pages "chapter and pages" output.check - new.block - format.number.series output - new.sentence - publisher "publisher" output.check - address output - } - { format.chapter.pages "chapter and pages" output.check - new.block - format.book.crossref output.nonnull - } - if$ - format.edition output - format.date "year" output.check - format.isbn output - format.doi output - format.url output - new.block - note output - fin.entry -} - -FUNCTION {incollection} -{ output.bibitem - format.authors "author" output.check - author format.key output - new.block - format.title "title" output.check - new.block - crossref missing$ - { format.in.ed.booktitle "booktitle" output.check - format.bvolume output - format.number.series output - format.chapter.pages output - new.sentence - publisher "publisher" output.check - address output - format.edition output - format.date "year" output.check - } - { format.incoll.inproc.crossref output.nonnull - format.chapter.pages output - } - if$ - format.isbn output - format.doi output - format.url output - new.block - note output - fin.entry -} - -FUNCTION {inproceedings} -{ output.bibitem - format.authors "author" output.check - author format.key output - new.block - format.title "title" output.check - new.block - crossref missing$ - { format.in.ed.booktitle "booktitle" output.check - format.bvolume output - format.number.series output - format.pages output - address empty$ - { organization publisher new.sentence.checkb - organization output - publisher output - format.date "year" output.check - } - { address output.nonnull - format.date "year" output.check - new.sentence - organization output - publisher output - } - if$ - } - { format.incoll.inproc.crossref output.nonnull - format.pages output - } - if$ - format.isbn output - format.doi output - format.url output - new.block - note output - fin.entry -} - -FUNCTION {conference} { inproceedings } - -FUNCTION {manual} -{ output.bibitem - format.authors output - author format.key output - new.block - format.btitle "title" output.check - organization address new.block.checkb - organization output - address output - format.edition output - format.date output - format.url output - new.block - note output - fin.entry -} - -FUNCTION {mastersthesis} -{ output.bibitem - format.authors "author" output.check - author format.key output - new.block - format.title "title" output.check - new.block - "Master's thesis" format.thesis.type output.nonnull - school "school" output.check - address output - format.date "year" output.check - format.url output - new.block - note output - fin.entry -} - -FUNCTION {misc} -{ output.bibitem - format.authors output - author format.key output - title howpublished new.block.checkb - format.title output - howpublished new.block.checka - howpublished output - format.date output - format.issn output - format.url output - new.block - note output - fin.entry - empty.misc.check -} - -FUNCTION {phdthesis} -{ output.bibitem - format.authors "author" output.check - author format.key output - new.block - format.btitle "title" output.check - new.block - "PhD thesis" format.thesis.type output.nonnull - school "school" output.check - address output - format.date "year" output.check - format.url output - new.block - note output - fin.entry -} - -FUNCTION {proceedings} -{ output.bibitem - format.editors output - editor format.key output - new.block - format.btitle "title" output.check - format.bvolume output - format.number.series output - address output - format.date "year" output.check - new.sentence - organization output - publisher output - format.isbn output - format.doi output - format.url output - new.block - note output - fin.entry -} - -FUNCTION {techreport} -{ output.bibitem - format.authors "author" output.check - author format.key output - new.block - format.title "title" output.check - new.block - format.tr.number output.nonnull - institution "institution" output.check - address output - format.date "year" output.check - format.url output - new.block - note output - fin.entry -} - -FUNCTION {unpublished} -{ output.bibitem - format.authors "author" output.check - author format.key output - new.block - format.title "title" output.check - new.block - note "note" output.check - format.date output - format.url output - fin.entry -} - -FUNCTION {default.type} { misc } - - -MACRO {jan} {"Jan."} - -MACRO {feb} {"Feb."} - -MACRO {mar} {"Mar."} - -MACRO {apr} {"Apr."} - -MACRO {may} {"May"} - -MACRO {jun} {"June"} - -MACRO {jul} {"July"} - -MACRO {aug} {"Aug."} - -MACRO {sep} {"Sept."} - -MACRO {oct} {"Oct."} - -MACRO {nov} {"Nov."} - -MACRO {dec} {"Dec."} - - - -MACRO {acmcs} {"ACM Comput. Surv."} - -MACRO {acta} {"Acta Inf."} - -MACRO {cacm} {"Commun. ACM"} - -MACRO {ibmjrd} {"IBM J. Res. Dev."} - -MACRO {ibmsj} {"IBM Syst.~J."} - -MACRO {ieeese} {"IEEE Trans. Softw. Eng."} - -MACRO {ieeetc} {"IEEE Trans. Comput."} - -MACRO {ieeetcad} - {"IEEE Trans. Comput.-Aided Design Integrated Circuits"} - -MACRO {ipl} {"Inf. Process. Lett."} - -MACRO {jacm} {"J.~ACM"} - -MACRO {jcss} {"J.~Comput. Syst. Sci."} - -MACRO {scp} {"Sci. Comput. Programming"} - -MACRO {sicomp} {"SIAM J. Comput."} - -MACRO {tocs} {"ACM Trans. Comput. Syst."} - -MACRO {tods} {"ACM Trans. Database Syst."} - -MACRO {tog} {"ACM Trans. Gr."} - -MACRO {toms} {"ACM Trans. Math. Softw."} - -MACRO {toois} {"ACM Trans. Office Inf. Syst."} - -MACRO {toplas} {"ACM Trans. Prog. Lang. Syst."} - -MACRO {tcs} {"Theoretical Comput. Sci."} - - -READ - -FUNCTION {sortify} -{ purify$ - "l" change.case$ -} - -INTEGERS { len } - -FUNCTION {chop.word} -{ 's := - 'len := - s #1 len substring$ = - { s len #1 + global.max$ substring$ } - 's - if$ -} - -FUNCTION {format.lab.names} -{ 's := - s #1 "{vv~}{ll}" format.name$ - s num.names$ duplicate$ - #2 > - { pop$ " et~al." * } - { #2 < - 'skip$ - { s #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" = - { " et~al." * } - { " and " * s #2 "{vv~}{ll}" format.name$ * } - if$ - } - if$ - } - if$ -} - -FUNCTION {author.key.label} -{ author empty$ - { key empty$ - { cite$ #1 #3 substring$ } - 'key - if$ - } - { author format.lab.names } - if$ -} - -FUNCTION {author.editor.key.label} -{ author empty$ - { editor empty$ - { key empty$ - { cite$ #1 #3 substring$ } - 'key - if$ - } - { editor format.lab.names } - if$ - } - { author format.lab.names } - if$ -} - -FUNCTION {author.key.organization.label} -{ author empty$ - { key empty$ - { organization empty$ - { cite$ #1 #3 substring$ } - { "The " #4 organization chop.word #3 text.prefix$ } - if$ - } - 'key - if$ - } - { author format.lab.names } - if$ -} - -FUNCTION {editor.key.organization.label} -{ editor empty$ - { key empty$ - { organization empty$ - { cite$ #1 #3 substring$ } - { "The " #4 organization chop.word #3 text.prefix$ } - if$ - } - 'key - if$ - } - { editor format.lab.names } - if$ -} - -FUNCTION {calc.short.authors} -{ type$ "book" = - type$ "inbook" = - or - 'author.editor.key.label - { type$ "proceedings" = - 'editor.key.organization.label - { type$ "manual" = - 'author.key.organization.label - 'author.key.label - if$ - } - if$ - } - if$ - 'short.list := -} - -FUNCTION {calc.label} -{ calc.short.authors - short.list - "(" - * - year duplicate$ empty$ - short.list key field.or.null = or - { pop$ "" } - 'skip$ - if$ - * - 'label := -} - -FUNCTION {sort.format.names} -{ 's := - #1 'nameptr := - "" - s num.names$ 'numnames := - numnames 'namesleft := - { namesleft #0 > } - { - s nameptr "{vv{ } }{ll{ }}{ f{ }}{ jj{ }}" format.name$ 't := - nameptr #1 > - { - " " * - namesleft #1 = t "others" = and - { "zzzzz" * } - { numnames #2 > nameptr #2 = and - { "zz" * year field.or.null * " " * } - 'skip$ - if$ - t sortify * - } - if$ - } - { t sortify * } - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ -} - -FUNCTION {sort.format.title} -{ 't := - "A " #2 - "An " #3 - "The " #4 t chop.word - chop.word - chop.word - sortify - #1 global.max$ substring$ -} - -FUNCTION {author.sort} -{ author empty$ - { key empty$ - { "to sort, need author or key in " cite$ * warning$ - "" - } - { key sortify } - if$ - } - { author sort.format.names } - if$ -} - -FUNCTION {author.editor.sort} -{ author empty$ - { editor empty$ - { key empty$ - { "to sort, need author, editor, or key in " cite$ * warning$ - "" - } - { key sortify } - if$ - } - { editor sort.format.names } - if$ - } - { author sort.format.names } - if$ -} - -FUNCTION {author.organization.sort} -{ author empty$ - { organization empty$ - { key empty$ - { "to sort, need author, organization, or key in " cite$ * warning$ - "" - } - { key sortify } - if$ - } - { "The " #4 organization chop.word sortify } - if$ - } - { author sort.format.names } - if$ -} - -FUNCTION {editor.organization.sort} -{ editor empty$ - { organization empty$ - { key empty$ - { "to sort, need editor, organization, or key in " cite$ * warning$ - "" - } - { key sortify } - if$ - } - { "The " #4 organization chop.word sortify } - if$ - } - { editor sort.format.names } - if$ -} - - -FUNCTION {presort} -{ calc.label - label sortify - " " - * - type$ "book" = - type$ "inbook" = - or - 'author.editor.sort - { type$ "proceedings" = - 'editor.organization.sort - { type$ "manual" = - 'author.organization.sort - 'author.sort - if$ - } - if$ - } - if$ - " " - * - year field.or.null sortify - * - " " - * - cite$ - * - #1 entry.max$ substring$ - 'sort.label := - sort.label * - #1 entry.max$ substring$ - 'sort.key$ := -} - -ITERATE {presort} - -SORT - -STRINGS { longest.label last.label next.extra } - -INTEGERS { longest.label.width last.extra.num number.label } - -FUNCTION {initialize.longest.label} -{ "" 'longest.label := - #0 int.to.chr$ 'last.label := - "" 'next.extra := - #0 'longest.label.width := - #0 'last.extra.num := - #0 'number.label := -} - -FUNCTION {forward.pass} -{ last.label label = - { last.extra.num #1 + 'last.extra.num := - last.extra.num int.to.chr$ 'extra.label := - } - { "a" chr.to.int$ 'last.extra.num := - "" 'extra.label := - label 'last.label := - } - if$ - number.label #1 + 'number.label := -} - -FUNCTION {reverse.pass} -{ next.extra "b" = - { "a" 'extra.label := } - 'skip$ - if$ - extra.label 'next.extra := - extra.label - duplicate$ empty$ - 'skip$ - { "{\natexlab{" swap$ * "}}" * } - if$ - 'extra.label := - label extra.label * 'label := -} - -EXECUTE {initialize.longest.label} - -ITERATE {forward.pass} - -REVERSE {reverse.pass} - -FUNCTION {bib.sort.order} -{ sort.label 'sort.key$ := -} - -ITERATE {bib.sort.order} - -SORT - -FUNCTION {begin.bib} -{ preamble$ empty$ - 'skip$ - { preamble$ write$ newline$ } - if$ - "\begin{thebibliography}{" number.label int.to.str$ * "}" * - write$ newline$ - "\providecommand{\natexlab}[1]{#1}" - write$ newline$ - "\providecommand{\url}[1]{\texttt{#1}}" - write$ newline$ - "\expandafter\ifx\csname urlstyle\endcsname\relax" - write$ newline$ - " \providecommand{\doi}[1]{doi: #1}\else" - write$ newline$ - " \providecommand{\doi}{doi: \begingroup \urlstyle{rm}\Url}\fi" - write$ newline$ -} - -EXECUTE {begin.bib} - -EXECUTE {init.state.consts} - -ITERATE {call.type$} - -FUNCTION {end.bib} -{ newline$ - "\end{thebibliography}" write$ newline$ -} - -EXECUTE {end.bib} diff --git a/manuscript/oxford/oup-authoring-template.cls b/manuscript/oxford/oup-authoring-template.cls deleted file mode 100644 index 5cde048..0000000 --- a/manuscript/oxford/oup-authoring-template.cls +++ /dev/null @@ -1,3211 +0,0 @@ -%% -%% Copyright 2025 Oxford University Press -%% -%% This file is part of the 'oup-authoring-template Bundle'. -%% --------------------------------------------- -%% -%% It may be distributed under the conditions of the LaTeX Project Public -%% License, either version 1.3 of this license or (at your option) any -%% later version. The latest version of this license is in -%% http://www.latex-project.org/lppl.txt -%% and version 1.2 or later is part of all distributions of LaTeX -%% version 1999/12/01 or later. -%% -%% The list of all files belonging to the 'oup-authoring-template Bundle' is -%% given in the file `manifest.txt'. -%% -%% Template article for Oxford University Press's document class `oup-authoring-template' -%% with bibliographic references -%% -%% Version 1.1, updated to reflect changes to OUP’s official page designs, 2022. -%% Version 1.2, updated to reflect changes to OUP’s official page designs, 2025. - -\newcommand\classname{oup-authoring-template} -\newcommand\lastmodifieddate{2025/11/17} -\newcommand\versionnumber{1.2} - -\NeedsTeXFormat{LaTeX2e}[2001/06/01] -\ProvidesClass{\classname}[\lastmodifieddate\space\versionnumber] - -% Are we printing crop marks? -\newif\if@cropmarkson \@cropmarksontrue -\newif\if@modern\global\@modernfalse -\newif\if@traditional\global\@traditionalfalse -\newif\if@contemporary\global\@contemporaryfalse -% -\newif\if@large\global\@largefalse -\newif\if@medium\global\@mediumfalse -\newif\if@mediumone\global\@mediumonefalse -\newif\if@small\global\@smallfalse -% -\newif\if@numbib \@numbibtrue -\newif\if@unnumsec\global\@unnumsecfalse -\newif\if@final\global\@finalfalse -\newif\if@weblink\global\@weblinkfalse -\newif\if@proof\global\@prooffalse% -\newif\if@otherarticle\global\@otherarticlefalse - -\if@compatibility\else -\DeclareOption{namedate}{\PassOptionsToPackage{authoryear,round}{natbib}\global\@numbibfalse} -\DeclareOption{numbered}{\PassOptionsToPackage{numbers,square}{natbib}\global\@numbibfalse} -\DeclareOption{draft}{\PassOptionsToPackage{draft}{graphicx}} -\DeclareOption{b4paper}{\PassOptionsToPackage{b4}{crop}} -\DeclareOption{centre}{\PassOptionsToPackage{center}{crop}} -\DeclareOption{crop}{\PassOptionsToPackage{cam}{crop}\global\@cropmarksontrue} -\DeclareOption{nocrop}{\PassOptionsToPackage{off}{crop}\global\@cropmarksonfalse} -\DeclareOption{info}{\PassOptionsToPackage{info}{crop}} -\DeclareOption{noinfo}{\PassOptionsToPackage{noinfo}{crop}} -\DeclareOption{final}{\global\@finaltrue} -\DeclareOption{unnumsec}{\global\@unnumsectrue} -\DeclareOption{otherarticle}{\global\@otherarticletrue} -% -\DeclareOption{webpdf}{\global\@weblinktrue} -% -\DeclareOption{modern}{\global\@moderntrue} -\DeclareOption{traditional}{\global\@traditionaltrue} -\DeclareOption{contemporary}{\global\@contemporarytrue} -% -\DeclareOption{large}{\global\@largetrue} -\DeclareOption{medium}{\global\@mediumtrue} -\DeclareOption{mediumone}{\global\@mediumtrue\global\@mediumonetrue} -\DeclareOption{small}{\global\@smalltrue} -\fi -\ExecuteOptions{b4paper,centre,info}% -\ProcessOptions - -\if@modern - \if@large - \setlength{\paperheight}{276truemm} - \setlength{\paperwidth}{210truemm} - \else - \if@medium - \setlength{\paperheight}{246truemm} - \setlength{\paperwidth}{189truemm} - \else - \if@small - \setlength{\paperheight}{234truemm} - \setlength{\paperwidth}{156truemm} - \else - \setlength{\paperheight}{146truemm} - \setlength{\paperwidth}{189truemm} - \fi - \fi - \fi -\else -\if@traditional - \if@large - \setlength{\paperheight}{276truemm} - \setlength{\paperwidth}{210truemm} - \else - \if@medium - \setlength{\paperheight}{246truemm} - \setlength{\paperwidth}{189truemm} - \else - \setlength{\paperheight}{234truemm} - \setlength{\paperwidth}{156truemm} - \fi - \fi -\else -\if@contemporary - \if@large - \setlength{\paperheight}{276truemm} - \setlength{\paperwidth}{210truemm} - \else - \if@medium - \setlength{\paperheight}{246truemm} - \setlength{\paperwidth}{189truemm} - \else - \if@small - \setlength{\paperheight}{234truemm} - \setlength{\paperwidth}{156truemm} - \else - \setlength{\paperheight}{246truemm} - \setlength{\paperwidth}{189truemm} - \fi - \fi - \fi -\else - \setlength{\paperheight}{278.83truemm} - \setlength{\paperwidth}{215.78truemm} -\fi\fi\fi - -% Load all necessary packages -\RequirePackage{crop} -\RequirePackage{graphicx} -\RequirePackage{caption} -\RequirePackage{amsmath} -\RequirePackage{array} -\RequirePackage{color} -\RequirePackage{xcolor} -\RequirePackage{amssymb} -\RequirePackage{flushend} -\RequirePackage{stfloats} -\RequirePackage[figuresright]{rotating} -\RequirePackage{chngpage} -\RequirePackage{totcount} -\RequirePackage{fix-cm} -\RequirePackage[bottom]{footmisc} - -\def\sffamilyfont{\sffamily} -\def\sffamilyfontitalic{\sffamily\itshape\selectfont} -\def\sffamilyfontbold{\sffamily\bfseries\selectfont} -\def\sffamilyfontbolditalic{\sffamily\bfseries\itshape\selectfont} -\def\sffamilyfontcn{\sffamily\fontseries{m}\fontshape{n}\selectfont} -\def\sffamilyfontcnitalic{\sffamily\fontseries{m}\fontshape{it}\selectfont} -\def\sffamilyfontcnbold{\sffamily\bfseries\selectfont} -\def\sffamilyfontcnbolditalic{\sffamily\fontseries{b}\fontshape{it}\selectfont} - -% Not sure if needed. -\newcommand\@ptsize{0} - -% Set twoside printing -\@twosidetrue - -% Marginal notes are on the outside edge -\@mparswitchfalse - -\reversemarginpar - -\if@modern - \if@large - \renewcommand\normalsize{\sffamilyfont% - \@setfontsize\normalsize{9bp}{11.5pt}% - \abovedisplayskip 11\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 6\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} - \else - \if@medium - \renewcommand\normalsize{\sffamilyfont% - \@setfontsize\normalsize{9bp}{11.5pt}% - \abovedisplayskip 11\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 6\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} - \else - \if@small - \renewcommand\normalsize{\sffamilyfont% - \@setfontsize\normalsize{9bp}{11.5pt}% - \abovedisplayskip 11\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 6\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} - \else - \fi - \fi - \fi -\else -\if@traditional - \if@large - \renewcommand\normalsize{% - \@setfontsize\normalsize{9bp}{11.75pt}% - \abovedisplayskip 11\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 6\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} - \else - \if@medium - \renewcommand\normalsize{% - \@setfontsize\normalsize{9bp}{11.75pt}% - \abovedisplayskip 11\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 6\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} - \else - \renewcommand\normalsize{% - \@setfontsize\normalsize{9bp}{11.75bp}% - \abovedisplayskip 11.75\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 11.75\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} - \fi - \fi -\else -\if@contemporary - \if@large - \renewcommand\normalsize{% - \@setfontsize\normalsize{8bp}{11.5bp}% - \abovedisplayskip 11.5\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 11.5\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} - \else% - \if@medium - \renewcommand\normalsize{% - \@setfontsize\normalsize{8bp}{11.5bp}% - \abovedisplayskip 11.5\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 11.5\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} - \else% - \renewcommand\normalsize{% - \@setfontsize\normalsize{8bp}{11bp}% - \abovedisplayskip 11\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 11\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} - \fi% - \fi% -\else - \renewcommand\normalsize{% - \@setfontsize\normalsize{8.5bp}{12pt}% - \abovedisplayskip 11\p@ \@plus2\p@ \@minus5\p@ - \abovedisplayshortskip \z@ \@plus3\p@ - \belowdisplayshortskip 6\p@ \@plus3\p@ \@minus3\p@ - \belowdisplayskip \abovedisplayskip - \let\@listi\@listI} -\fi -\fi -\fi -\normalsize -\let\@bls\baselineskip - -\newcommand\small{% - \@setfontsize\small{7}{10}% - \abovedisplayskip 10\p@ minus 3\p@ - \belowdisplayskip \abovedisplayskip - \abovedisplayshortskip \z@ plus 2\p@ - \belowdisplayshortskip 4\p@ plus 2\p@ minus2\p@ - \def\@listi{\topsep 4.5\p@ plus 2\p@ minus 1\p@ - \itemsep \parsep - \topsep 4\p@ plus 2\p@ minus 2\p@}} - -\if@modern - \newcommand\footnotesize{% - \@setfontsize\footnotesize{7.5bp}{8}% - \abovedisplayskip 6\p@ minus 3\p@ - \belowdisplayskip\abovedisplayskip - \abovedisplayshortskip \z@ plus 3\p@ - \belowdisplayshortskip 6\p@ plus 3\p@ minus 3\p@ - \def\@listi{\topsep 3\p@ plus 1\p@ minus 1\p@ - \parsep 2\p@ plus 1\p@ minus 1\p@\itemsep \parsep}} -\else -\if@contemporary - \newcommand\footnotesize{% - \@setfontsize\footnotesize{6.5}{9.5}% - \abovedisplayskip 6\p@ minus 3\p@ - \belowdisplayskip\abovedisplayskip - \abovedisplayshortskip \z@ plus 3\p@ - \belowdisplayshortskip 6\p@ plus 3\p@ minus 3\p@ - \def\@listi{\topsep 3\p@ plus 1\p@ minus 1\p@ - \parsep 2\p@ plus 1\p@ minus 1\p@\itemsep \parsep}} -\else -\if@traditional - \newcommand\footnotesize{% - \@setfontsize\footnotesize{7.5bp}{9.5bp}% - \abovedisplayskip 9.5\p@ minus 3\p@ - \belowdisplayskip\abovedisplayskip - \abovedisplayshortskip \z@ plus 3\p@ - \belowdisplayshortskip 6\p@ plus 3\p@ minus 3\p@ - \def\@listi{\topsep 9.5\p@ plus 1\p@ minus 1\p@ - \parsep 2\p@ plus 1\p@ minus 1\p@\itemsep \parsep}} -\fi -\fi -\fi - -\def\scriptsize{\@setfontsize\scriptsize{6.5pt}{9.5pt}} -\def\tiny{\@setfontsize\tiny{5pt}{7pt}} -\def\large{\@setfontsize\large{11.5pt}{12pt}} -\def\Large{\@setfontsize\Large{14pt}{16}} -\def\LARGE{\@setfontsize\LARGE{15pt}{17pt}} -\def\huge{\@setfontsize\huge{22pt}{22pt}} -\def\Huge{\@setfontsize\Huge{30pt}{30pt}} - -\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm} -\DeclareOldFontCommand{\sf}{\normalfont\sffamilyfont}{\mathsf} -\DeclareOldFontCommand{\sfit}{\normalfont\sffamily\itshape}{\mathsf} -\DeclareOldFontCommand{\sfb}{\normalfont\sffamilyfontbold}{\mathsf} -\DeclareOldFontCommand{\sfbi}{\normalfont\sffamily\bfseries\itshape}{\mathsf} -\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt} -\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf} -\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit} -\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl} -\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc} - -% Crop Here -\def\oddsideskip{48pt}% -\def\evensideskip{56pt}% - -\newdimen\croppaperwidth -\newdimen\croppaperheight -\setlength{\croppaperwidth}{\paperwidth} -\setlength{\croppaperheight}{\paperheight} -\if@weblink%% -\else% - \addtolength{\croppaperwidth}{28truemm}% - \addtolength{\croppaperheight}{28truemm}% -\fi% - \CROP@size{\croppaperwidth}{\croppaperheight}% -% -\if@weblink%% -\else -\renewcommand*\CROP@@ulc{% - \begin{picture}(0,0) - \unitlength\p@\thinlines - \put(-40,0){\line(1,0){30.65}} - \put(0,42){\line(0,-1){30.65}} - \end{picture}% -} -\renewcommand*\CROP@@urc{% - \begin{picture}(0,0) - \unitlength\p@\thinlines - \put(41,0){\line(-1,0){30.65}} - \put(0,42){\line(0,-1){30.65}} - \end{picture}% -} -\renewcommand*\CROP@@llc{% - \begin{picture}(0,0) - \unitlength\p@\thinlines - \put(-40,0){\line(1,0){30.65}} - \put(0,-40){\line(0,1){30.65}} - \end{picture}% -} -\renewcommand*\CROP@@lrc{% - \begin{picture}(0,0) - \unitlength\p@\thinlines - \put(41,0){\line(-1,0){30.65}} - \put(0,-40){\line(0,1){30.65}} - \end{picture}% -} -% -\renewcommand*\CROP@@info{{% - \global\advance\CROP@index\@ne - \def\x{\discretionary{}{}{\hbox{\kern.5em--\kern.5em}}}% - \advance\paperwidth-20\p@ - \dimen@10pt - \ifx\CROP@pagecolor\@empty - \else - \advance\dimen@\CROP@overlap - \fi - \hb@xt@\z@{% - \hss - \vbox to\z@{% - %\centering - \hsize\paperwidth - \vss - \normalfont - \normalsize - \expandafter\csname\CROP@font\endcsname{\ifodd\c@page\hfill\else\hspace*{\evensideskip}\fi\if@proof\noindent\fboxsep1\p@\fbox{\fboxsep2\p@\fbox{\@oupdraftcopy}}\else\fi\ifodd\c@page\hspace*{\oddsideskip}\else\fi}%\noindent\fbox{\fboxsep2\p@\fbox{\@oupdraftcopy}} - \vskip\dimen@ - }% - \hss - }% -}} -% -\crop[cam]% -\fi% -% -\newdimen\Croppdfwidth -\newdimen\Croppdfheight -\newdimen\Trimpdfwidth -\newdimen\Trimpdfheight -\Croppdfwidth=\croppaperwidth -\Croppdfheight=\croppaperheight -\advance\Croppdfwidth by -0.71mm -\advance\Croppdfheight by -0.92mm -\Trimpdfwidth=\paperwidth -\Trimpdfheight=\paperheight -\advance\Trimpdfwidth by -0.59mm -\advance\Trimpdfheight by -0.88mm -% -\newdimen\CP@toff@wd -\newdimen\CP@toff@ht -% -\newdimen\CP@boff@wd -\newdimen\CP@boff@ht -% -\newdimen\CP@crop@wd -\newdimen\CP@crop@ht -% -\newdimen\CP@bled@wd -\newdimen\CP@bled@ht -% -\newdimen\CP@trim@wd -\newdimen\CP@trim@ht -% -\def\str@yes{yes} -\def\SetCrop#1#2{% - \gdef\IsCropSet{yes} - \global\CP@crop@wd=#1\relax - \global\CP@crop@ht=#2\relax} -\def\SetTrim#1#2{% - \gdef\IsTrimSet{yes} - \global\CP@trim@wd=#1\relax - \global\CP@trim@ht=#2\relax} -\def\SetBleed#1#2{% - \gdef\IsBleedSet{yes} - \global\CP@bled@wd=#1\relax - \global\CP@bled@ht=#2\relax} -% -\everyjob\expandafter{% - \the\everyjob - \typeout{% - \filename\space <\filedate>^^J - Version: v\fileversion^^J - LaTeX macros for setting Page Box parameters - }% - \IfFileExists{\filename.cfg}{% - \begingroup\@@input\@filef@und\endgroup - }{% - \typeout{No File: \filename.cfg}% - }% - \IfFileExists{\jobname.cfg}{% - \begingroup\@@input\@filef@und\endgroup - }{% - \typeout{No File: \jobname.cfg}% - }% -} -% -\if@weblink%%% - \SetCrop{\Trimpdfwidth}{\Trimpdfheight}% - \SetTrim{\Trimpdfwidth}{\Trimpdfheight}% - \SetBleed{0mm}{0mm}% -\else% - \SetCrop{\Croppdfwidth}{\Croppdfheight}% - \SetTrim{\Trimpdfwidth}{\Trimpdfheight}% - \SetBleed{3mm}{3mm}% -\fi -% -% -\def\do@pagebox@calc{% - \CP@toff@wd=\CP@crop@wd - \advance\CP@toff@wd by -\CP@trim@wd - \divide\CP@toff@wd by \tw@ -% - \CP@toff@ht=\CP@crop@ht - \advance\CP@toff@ht by -\CP@trim@ht - \divide\CP@toff@ht by \tw@ -% - \advance\CP@trim@wd by \CP@toff@wd - \advance\CP@trim@ht by \CP@toff@ht -% - \CP@boff@wd=\CP@toff@wd - \advance\CP@boff@wd by -\CP@bled@wd -% - \CP@boff@ht=\CP@toff@ht - \advance\CP@boff@ht by -\CP@bled@ht -% - \advance\CP@bled@wd by \CP@trim@wd - \advance\CP@bled@ht by \CP@trim@ht -} -% -\def\pdf@page@parameters{% - \ifx\IsCropSet\str@yes - [{ThisPage} << /CropBox [0 0 \strip@pt\CP@crop@wd\space \strip@pt\CP@crop@ht] >> /PUT pdfmark - [{ThisPage} << /MediaBox[0 0 \strip@pt\CP@crop@wd\space \strip@pt\CP@crop@ht] >> /PUT pdfmark - \fi - \ifx\IsTrimSet\str@yes - [{ThisPage} << /TrimBox [\strip@pt\CP@toff@wd\space \strip@pt\CP@toff@ht\space \strip@pt\CP@trim@wd\space \strip@pt\CP@trim@ht] >> /PUT pdfmark - \fi - \ifx\IsBleedSet\str@yes - [{ThisPage} << /BleedBox[\strip@pt\CP@boff@wd\space \strip@pt\CP@boff@ht\space \strip@pt\CP@bled@wd\space \strip@pt\CP@bled@ht] >> /PUT pdfmark - \fi -} -% -\def\shipout@PageObjects{% - \special{ps: \pdf@page@parameters}% -} -%% -\AtBeginDocument{% - \do@pagebox@calc - \let\org@begindvi\@begindvi - \def\@begindvi{% - \shipout@PageObjects - \org@begindvi - \global\let\@begindvi\rest@dvi@pages - }% -} -\let\rest@dvi@pages\shipout@PageObjects -% Crop End here - -\setlength\lineskip{1\p@} -\setlength\normallineskip{1\p@} -\renewcommand\baselinestretch{} -\setlength\parskip{0\p@} -\if@contemporary% - \setlength\parindent{1em}% -\else% -\if@traditional - \setlength\parindent{8pt}% -\else -\if@modern - \setlength\parindent{12pt}% -\else - \setlength\parindent{12pt}% -\fi% -\fi% -\fi -% -\setlength\smallskipamount{3\p@ \@plus 1\p@ \@minus 1\p@} -\setlength\medskipamount{6\p@ \@plus 2\p@} -\setlength\bigskipamount{12\p@ \@plus 4\p@ \@minus 4\p@} -\@lowpenalty 51 -\@medpenalty 151 -\@highpenalty 301 -\clubpenalty 10000 -\widowpenalty 10000 -\displaywidowpenalty 100 -\predisplaypenalty 10000 -\postdisplaypenalty 2500 -\interlinepenalty 0 -\brokenpenalty 10000 -\lefthyphenmin=3 -\righthyphenmin=3 - -\if@modern - \if@large - \setlength\headheight{16\p@} - \setlength\topmargin{9.2mm} - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{11.4\p@} - \setlength\footskip{16\p@} - \setlength\maxdepth{.5\topskip} - \setlength\textwidth{178mm} - \setlength\textheight{59\baselineskip} - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{4pc} - \addtolength\oddsidemargin{-1in} - \setlength\@tempdima{\paperwidth} - \addtolength\@tempdima{-\textwidth} - \addtolength\@tempdima{-4pc} - \setlength\evensidemargin{\@tempdima} - \addtolength\evensidemargin{-1in} - \setlength\columnsep{6mm} - \setlength\columnseprule{0\p@} - \else - \if@medium - \setlength\headheight{16\p@} - \setlength\topmargin{24pt} - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{10.5\p@} - \setlength\footskip{0\p@} - \setlength\maxdepth{.5\topskip} - \setlength\textwidth{157mm} - \setlength\textheight{52\baselineskip} - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{17mm} - \addtolength\oddsidemargin{-1in} - \setlength\@tempdima{\paperwidth} - \addtolength\@tempdima{-\textwidth} - \addtolength\@tempdima{-17mm} - \setlength\evensidemargin{\@tempdima} - \addtolength\evensidemargin{-.89in} - \setlength\columnsep{5mm} - \setlength\columnseprule{0\p@} - \else - \if@small - \setlength\headheight{16\p@} - \setlength\topmargin{2.25pc} - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{13.6\p@} - \setlength\footskip{16\p@} - \setlength\maxdepth{.5\topskip} - \setlength\textwidth{124mm} - \setlength\textheight{49\baselineskip} - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{17mm} - \addtolength\oddsidemargin{-1in} - \setlength\@tempdima{\paperwidth} - \addtolength\@tempdima{-\textwidth} - \addtolength\@tempdima{-19.6mm} - \setlength\evensidemargin{\@tempdima} - \addtolength\evensidemargin{-.89in} - \setlength\columnsep{14.5pt} - \setlength\columnseprule{0\p@} - \else - \fi - \fi - \fi -\else -\if@traditional - \if@large - \setlength\headheight{16\p@} - \setlength\topmargin{6.2mm} - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{18.5\p@} - \setlength\footskip{16\p@} - \setlength\maxdepth{.5\topskip} - \setlength\textwidth{178mm} - \setlength\textheight{58\baselineskip} - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{17mm} - \addtolength\oddsidemargin{-1in} - \setlength\@tempdima{\paperwidth} - \addtolength\@tempdima{-\textwidth} - \addtolength\@tempdima{-17mm} - \setlength\evensidemargin{\@tempdima} - \addtolength\evensidemargin{-1in} - \setlength\columnsep{6mm} - \setlength\columnseprule{0\p@} - \else - \if@medium - \if@mediumone%RK-new - \setlength\headheight{16\p@} - \setlength\topmargin{5.5mm}%%%RK-new - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{20.4\p@}%%RK-new - \setlength\footskip{16\p@} - \setlength\maxdepth{.5\topskip} - \setlength\textwidth{136mm} - \setlength\textheight{51\baselineskip}%RK-new - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{28mm}%RK-new - \addtolength\oddsidemargin{-1in} - \setlength\@tempdima{\paperwidth} - \addtolength\@tempdima{-\textwidth} - \addtolength\@tempdima{-28mm}%RK-new - \setlength\evensidemargin{\@tempdima} - \addtolength\evensidemargin{-1in} - \setlength\columnsep{18pt} - \setlength\columnseprule{0\p@} - \else - \setlength\headheight{16\p@} - \setlength\topmargin{5.5mm}%%%RK-new - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{17.6\p@}%%RK-new - \setlength\footskip{16\p@} - \setlength\maxdepth{.5\topskip} - \setlength\textwidth{160mm} - \setlength\textheight{51\baselineskip}%RK-new - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{16mm}%RK-new - \addtolength\oddsidemargin{-1in} - \setlength\@tempdima{\paperwidth} - \addtolength\@tempdima{-\textwidth} - \addtolength\@tempdima{-16mm}%RK-new - \setlength\evensidemargin{\@tempdima} - \addtolength\evensidemargin{-1in} - \setlength\columnsep{18pt} - \setlength\columnseprule{0\p@} - \fi - \else - \setlength\headheight{16\p@} - \setlength\topmargin{5.5mm} - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{7.5mm} - \setlength\footskip{16\p@} - \setlength\maxdepth{2pt} - \setlength\textwidth{127truemm} - \setlength\textheight{48\baselineskip} -% \addtolength\textheight{-10bp} - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{16mm} - \addtolength\oddsidemargin{-1in} - \setlength\evensidemargin{13mm} - \addtolength\evensidemargin{-1in} - \setlength\columnsep{18pt} - \setlength\columnseprule{0\p@} - \fi - \fi -\else -\if@contemporary - \if@large - \setlength\headheight{16\p@} - \setlength\topmargin{7.1mm} - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{5.8mm} - \setlength\footskip{16\p@} - \setlength\maxdepth{2pt}%.5\topskip} - \setlength\textwidth{185mm} - \setlength\textheight{58\baselineskip} -% \addtolength\textheight{-11bp} - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{12.5mm} - \addtolength\oddsidemargin{-1in} - \setlength\evensidemargin{12.5mm} - \addtolength\evensidemargin{-1in}% - \setlength\columnsep{6mm} - \setlength\columnseprule{0\p@} - \else - \if@medium - \if@mediumone - \setlength\headheight{16\p@} - \setlength\topmargin{9.4mm} - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{5.5mm} - \setlength\footskip{16\p@} - \setlength\maxdepth{2pt}%.5\topskip} - \setlength\textwidth{135mm} - \setlength\textheight{49\baselineskip} -% \addtolength\textheight{-11bp} - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{27mm} - \addtolength\oddsidemargin{-1in} - \setlength\evensidemargin{27mm} - \addtolength\evensidemargin{-1in}% - \setlength\columnsep{5mm} - \setlength\columnseprule{0\p@} - \else - \setlength\headheight{16\p@} - \setlength\topmargin{9.4mm} - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{5.5mm} - \setlength\footskip{16\p@} - \setlength\maxdepth{2pt}%.5\topskip} - \setlength\textwidth{149mm} - \setlength\textheight{50\baselineskip} - % \addtolength\textheight{-11bp} - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{20mm} - \addtolength\oddsidemargin{-1in} - \setlength\evensidemargin{20mm} - \addtolength\evensidemargin{-1in}% - \setlength\columnsep{5mm} - \setlength\columnseprule{0\p@} - \fi - \else - \if@small - \setlength\headheight{16\p@} - \setlength\topmargin{6.4mm} - \addtolength\topmargin{-1in} - \setlength\topskip{10\p@} - \setlength\headsep{4.5mm} - \setlength\footskip{16\p@} - \setlength\maxdepth{2pt}%.5\topskip} - \setlength\textwidth{126mm} - \setlength\textheight{51\baselineskip} -% \addtolength\textheight{-11bp} - \setlength\marginparsep{3\p@} - \setlength\marginparpush{3\p@} - \setlength\marginparwidth{35\p@} - \setlength\oddsidemargin{15mm} - \addtolength\oddsidemargin{-1in} - \setlength\evensidemargin{15mm} - \addtolength\evensidemargin{-1in}% - \setlength\columnsep{14.5pt} - \setlength\columnseprule{0\p@} - \else - \fi - \fi - \fi -\else -\fi -\fi -\fi - -\addtolength\textheight{\topskip} -\if@contemporary - \setlength\footnotesep{7\p@} -\else -\if@traditional - \setlength\footnotesep{6.5\p@} -\else - \setlength\footnotesep{9\p@} -\fi -\fi -\setlength{\skip\footins}{12\p@ \@plus 6\p@ \@minus 1\p@} -\setcounter{totalnumber}{10} -\setcounter{topnumber}{5} -\setcounter{bottomnumber}{5} -\renewcommand\topfraction{.9} -\renewcommand\bottomfraction{.9} -\renewcommand\textfraction{.06} -\renewcommand\floatpagefraction{.94} -\renewcommand\dbltopfraction{.9} -\renewcommand\dblfloatpagefraction{.9} -\setlength\floatsep {12\p@ \@plus 2\p@ \@minus 2\p@} -\setlength\textfloatsep{20\p@ \@plus 2\p@ \@minus 4\p@} -\setlength\intextsep {18\p@ \@plus 2\p@ \@minus 2\p@} -\setlength\dblfloatsep {12\p@ \@plus 2\p@ \@minus 2\p@} -\setlength\dbltextfloatsep{20\p@ \@plus 2\p@ \@minus 4\p@} - -\setlength\@fptop{0\p@} -\setlength\@fpsep{12\p@ \@plus 1fil} -\setlength\@fpbot{0\p@} - -\setlength\@dblfptop{0\p@} -\setlength\@dblfpsep{12\p@ \@plus 1fil} -\setlength\@dblfpbot{0\p@} - -\DeclareMathSizes{5} {5} {5} {5} -\DeclareMathSizes{6} {6} {5} {5} -\DeclareMathSizes{7} {7} {5} {5} -\DeclareMathSizes{8} {8} {6} {5} -\DeclareMathSizes{9} {9} {6.5} {5} -\DeclareMathSizes{10} {10} {7.5} {5} -\DeclareMathSizes{12} {12} {9} {7} - -\def\ps@headings - {% - \let\@oddfoot\@empty% - \let\@evenfoot\@empty% - \if@traditional - \if@large - \def\@evenhead{\vbox{\hbox to \textwidth{\fontsize{9.5bp}{12}\selectfont% - {\textbf{\thepage}}{\hspace*{9pt}{\textbullet}\hspace*{9pt}}{\itshape\strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}\hfill}}}% - \def\@oddhead{\vbox{\hbox to \textwidth{\fontsize{9.5bp}{12}\selectfont% - \hfill{{\itshape\strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}}{\hspace*{9pt}{\textbullet}\hspace*{9pt}}{\textbf{\thepage}}}% - }}% - \else - \if@medium - \def\@evenhead{\vbox{\hbox to \textwidth{\fontsize{9.5bp}{12}\selectfont - {\fontsize{9bp}{12}\selectfont\textbf{\thepage}}{\hspace*{9pt}{\textbullet}\hspace*{9pt}}{\itshape\strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}\hfill}}}% - \def\@oddhead{\vbox{\hbox to \textwidth{\fontsize{9.5bp}{12}\selectfont - \hfill{{\itshape\strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}}{\hspace*{9pt}{\textbullet}\hspace*{9pt}}{\fontsize{9bp}{12}\selectfont\textbf{\thepage}}}% - }}% - \else - \def\@evenhead{\hbox to \textwidth{{\textbf{\thepage}}{\hspace*{9pt}{\textbullet}\hspace*{9pt}}{\strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}\hfill}}% - \def\@oddhead{\hbox to \textwidth{\hfill{\textit{\rightmark}, Volume \@vol, Issue \@issue}{\hspace*{9pt}{\textbullet}\hspace*{9pt}}\textbf{\thepage}}% - }% - \fi - \fi - \else - \if@contemporary - \def\@evenhead{\hbox to \textwidth{\sffamily\fontsize{8bp}{10bp}\selectfont% - {{\fontseries{m}\fontsize{9bp}{10bp}\selectfont\thepage}}\hspace*{4pt}\raisebox{-1.5pt}{\rule{.3pt}{8pt}}\hspace*{4pt}{\strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}\hfill}}% - \def\@oddhead{\hbox to \textwidth{\hfill\sffamily\fontsize{8bp}{10bp}\selectfont - {{\strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}}\hspace*{4pt}\raisebox{-1.5pt}{\rule{.3pt}{8pt}}\hspace*{4pt}{{\fontseries{m}\fontsize{9bp}{10bp}\selectfont\thepage}}% - }}% - \else - \if@modern - \def\@evenhead{\vbox{\hbox to \textwidth{\if@medium\fontsize{6.5bp}{6.5}\selectfont\else\fontsize{8bp}{10}\selectfont\fi - {\sffamilyfontbold{\selectfont\thepage}}\hfill\sffamilyfontitalic{\fontshape{it}\selectfont - \strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}}\if@medium\vspace{5.5\p@}\else\vspace{8\p@}\fi\color{black!60}\rule{\textwidth}{1\p@}}}% - \def\@oddhead{\vbox{\hbox to \textwidth{\if@medium\fontsize{6.5bp}{6.5}\selectfont\else\fontsize{8bp}{10}\selectfont\fi - {\sffamilyfontitalic{\fontshape{it}\selectfont\strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}}\hfill{\sffamilyfontbold{\thepage}}}% - \if@medium\vspace{5.5\p@}\else\vspace{8\p@}\fi\color{black!60}\rule{\textwidth}{1\p@}}}% - \else - \def\@evenhead{\vbox{\hbox to \textwidth{\fontsize{8bp}{10}\selectfont - {\sffamilyfontbold{\selectfont\thepage}}\hfill\sffamilyfontitalic{\fontshape{it}\selectfont - \strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}}\vspace{5\p@}\rule{\textwidth}{1\p@}}}% - \def\@oddhead{\vbox{\hbox to \textwidth{\fontsize{8bp}{10}\selectfont - {\sffamilyfontitalic{\fontshape{it}\selectfont\strut\@journaltitle, \@pubyear,\ Volume \@vol,\ Issue \@issue}}\hfill{\sffamilyfontbold{\thepage}}}% - \vspace{5\p@}\rule{\textwidth}{1\p@}}}% - \fi\fi\fi - \def\titlemark##1{\markboth{##1}{##1}}% - } - -\def\ps@opening{% - \if@modern - \def\@oddhead{\raisebox{-10.3mm}[0pt][0pt]{\hbox to \textwidth{%{\color{black!50}\rule{1.5mm}{19mm}}\hskip2.5mm% - {\vbox to 19mm{\hbox{}\vfill\hbox to \if@small108mm\else122mm\fi{{% - \parbox{290pt}{\raggedright{{\sffamily\fontsize{6.5bp}{9.5bp}\selectfont\textit{\@journaltitle},\ \@copyrightyear, \@volume, \thepage--\thelastpage\par\vskip0pt}} - {\sffamily\fontsize{6.5bp}{9.bp}\selectfont\@DOI\par% - \ifx\@access\@empty\else\@access\par\fi\vskip0pt - \ifx\@specialissue\@empty\else\@specialissue\par\fi - \ifx\@appnotes\@empty\else\sffamily\fontsize{7bp}{9.bp}\textbf{\@appnotes}\par\fi - } - }}% - }\vfill\hbox{}}}\hfill{\color{black!20}\rule{45pt}{55pt}}}}}% - \def\@evenhead{\raisebox{-10.3mm}[0pt][0pt]{\hbox to \textwidth{%{\color{black!50}\rule{1.5mm}{19mm}}\hskip2.5mm% - {\vbox to 19mm{\hbox{}\vfill\hbox to \if@small108mm\else122mm\fi{{% - \parbox{290pt}{\raggedright{{\sffamily\fontsize{8bp}{10bp}\selectfont\textit{\@journaltitle},\ \@copyrightyear, \@volume, \thepage--\thelastpage\par\vskip0pt}} - {\sffamily\fontsize{7bp}{9bp}\selectfont\@DOI\par% - \ifx\@access\@empty\else\@access\par\fi\vskip0pt - \ifx\@specialissue\@empty\else\@specialissue\par\fi - \ifx\@appnotes\@empty\else\textbf{\@appnotes}\par\fi} - }}% - }\vfill\hbox{}}}\hfill{\color{black!20}\rule{45pt}{55pt}}}}} - \fi - \if@traditional - \def\@oddhead{\raisebox{\if@large-6pt\else\if@medium-6pt\else-6pt\fi\fi}[0pt][0pt]{% - \hbox to \textwidth{{% - \parbox{0.8\textwidth}{\raggedright\fontsize{7bp}{10bp}\selectfont - \textit{\@journaltitle},\ \@copyrightyear, pp. \thepage--\thelastpage\par - \@DOI\par\ifx\@access\@empty\else\@access\par\fi% - \ifx\@appnotes\@empty\else\textbf{\@appnotes}\par\fi} - }% - \hfill\hbox{\color{black!20}\rule[-7.5pt]{55pt}{20pt}}}}}% - \def\@evenhead{\raisebox{\if@large-6pt\else\if@medium-6pt\else-6pt\fi\fi}[0pt][0pt]{% - \hbox to \textwidth{{% - \parbox{0.8\textwidth}{\raggedright\fontsize{7bp}{10bp}\selectfont - \textit{\@journaltitle},\ \@copyrightyear, pp. \thepage--\thelastpage\par - \@DOI\par\ifx\@access\@empty\else\@access\par\fi% - \ifx\@appnotes\@empty\else\textbf{\@appnotes}\par\fi} - }% - \hfill\hbox{\color{black!20}\rule{55pt}{20pt}}}}} - \fi - \if@contemporary - \def\@oddhead{\raisebox{\if@large-18.8mm\else\if@medium-19.1mm\else-17mm\fi\fi}[0pt][0pt]{\hbox to \textwidth{{\color{black!50}\rule{1.5mm}{19mm}}\hskip2.5mm% - {\vbox to 19mm{\vskip2.5pt\hbox to \textwidth{{% - \parbox{290pt}{\raggedright{{\sffamily\fontsize{8bp}{10bp}\selectfont\textit{\@journaltitle},\ \@copyrightyear, pp. \thepage--\thelastpage\par\vskip3pt}} - {\sffamily\fontsize{7bp}{9bp}\selectfont\@DOI\par% - \ifx\@access\@empty\else\@access\par\fi\vskip3pt - \ifx\@appnotes\@empty\else\textbf{\@appnotes}\par\fi} - }}% - \hfill\hbox{\color{black!20}\rule{55pt}{20pt}}\hskip4mm}\vfill\hbox{}}}}}}% - \def\@evenhead{\raisebox{\if@large-18.8mm\else\if@medium-19.1mm\else-17mm\fi\fi}[0pt][0pt]{\hbox to \textwidth{{\color{black!50}\rule{1.5mm}{19mm}}\hskip2.5mm% - {\vbox to 19mm{\vskip2.5pt\hbox to \textwidth{{% - \parbox{290pt}{\raggedright{{\sffamily\fontsize{8bp}{10bp}\selectfont\textit{\@journaltitle},\ \@copyrightyear, pp. \thepage--\thelastpage\par\vskip3pt}} - {\sffamily\fontsize{7bp}{9bp}\selectfont\@DOI\par% - \ifx\@access\@empty\else\@access\par\fi\vskip3pt - \ifx\@appnotes\@empty\else\textbf{\@appnotes}\par\fi} - }}% - \hfill\hbox{\color{black!20}\rule{55pt}{20pt}}\hskip4mm}\vfill\hbox{}}}}}} - \fi -% -\if@modern - \def\@oddfoot{\parbox{\textwidth}{%\if@medium\if@mediumone\vspace*{0pt}\else\vspace*{-23.5pt}\fi\else\if@small\vspace*{0pt}\else\vspace*{-37.5pt}\fi\fi -\textcolor{black!60}{\rule{\textwidth}{.5pt}}\par\vspace*{5pt}\fontsize{6.25bp}{8bp}\selectfont\@history\par\raggedright - \fontseries{m}\selectfont$\copyright$ The Author(s) \@pubyear. Copyright and licence statements to be updated by the publisher during production. - }} - \def\@evenfoot{\parbox{\textwidth}{%\if@medium\if@mediumone\vspace*{0pt}\else\vspace*{-23.5pt}\fi\else\if@small\vspace*{0pt}\else\vspace*{-37.5pt}\fi\fi -\textcolor{black!60}{\rule{\textwidth}{.5pt}}\par\vspace*{5pt}\fontsize{6.25bp}{8bp}\selectfont\@history\par\raggedright - \fontseries{m}\selectfont$\copyright$ The Author(s) \@pubyear. Copyright and licence statements to be updated by the publisher during production. - }} - \else - \def\@evenfoot{\if@contemporary\raisebox{0mm}[0pt][0pt]{\parbox{\textwidth}{\noindent\rule{\textwidth}{.5pt}\par% - \vskip1.5pt\sffamily\fontsize{6.5bp}{9.5bp}\selectfont\@history\par\raggedright% - \noindent\copyright\space The Author(s) \@copyrightyear. Copyright and licence statements to be updated by the publisher during production.}}% - \else% - \if@traditional% - \raisebox{0mm}[0pt][0pt]{\parbox{\textwidth}{\noindent\rule{\textwidth}{.5pt}\par% - \vskip2.5pt\fontsize{6.25bp}{8.25bp}\selectfont\@history\par\raggedright% - \noindent\copyright\space The Author(s) \@copyrightyear. Copyright and licence statements to be updated by the publisher during production.}}% - \fi\fi% - } -% - \def\@oddfoot{\if@contemporary\raisebox{0mm}[0pt][0pt]{\parbox{\textwidth}{\noindent\rule{\textwidth}{.5pt}\par% - \vskip1.5pt\sffamily\fontsize{6.5bp}{9.5bp}\selectfont\@history\par\raggedright% - \noindent\copyright\space The Author(s) \@copyrightyear. Copyright and licence statements to be updated by the publisher during production.}}% - \else% - \if@traditional% - \raisebox{0mm}[0pt][0pt]{\parbox{\textwidth}{\noindent\rule{\textwidth}{.5pt}\par% - \vskip2.5pt\fontsize{6.25bp}{8.25bp}\selectfont\@history\par\raggedright% - \noindent\copyright\space The Author(s) \@copyrightyear. Copyright and licence statements to be updated by the publisher during production.}}% - \fi\fi% - } -\fi -} - -% Page range -\newif\iflastpagegiven \lastpagegivenfalse -\newcommand\firstpage[1]{% - \gdef\@firstpage{#1}% - \ifnum\@firstpage>\c@page - \setcounter{page}{#1}% - \ClassWarning{BIO}{Increasing pagenumber to \@firstpage}% - \else \ifnum\@firstpage<\c@page - \ClassWarning{BIO}{Firstpage lower than pagenumber}\fi\fi - \xdef\@firstpage{\the\c@page}% - } -\def\@firstpage{1} -\def\pagenumbering#1{% - \global\c@page \@ne - \gdef\thepage{\csname @#1\endcsname \c@page}% - \gdef\thefirstpage{% - \csname @#1\endcsname \@firstpage}% - \gdef\thelastpage{% - \csname @#1\endcsname \@lastpage}% - } - -\newcommand\lastpage[1]{\xdef\@lastpage{#1}% - \global\lastpagegiventrue} -\def\@lastpage{0} -\def\setlastpage{\iflastpagegiven\else - \edef\@tempa{@lastpage@}% - \expandafter - \ifx \csname \@tempa \endcsname \relax - \gdef\@lastpage{0}% - \else - \xdef\@lastpage{\@nameuse{@lastpage@}}% - \fi - \fi } -\def\writelastpage{% - \iflastpagegiven \else - \immediate\write\@auxout% - {\string\global\string\@namedef{@lastpage@}{\the\c@page}}% - \fi - } -\def\thepagerange{% - \ifnum\@lastpage =0 {\ \bf ???} \else - \ifnum\@lastpage = \@firstpage \ \thefirstpage\else - \thefirstpage--\thelastpage \fi\fi} - -\AtBeginDocument{\setlastpage - \pagenumbering{arabic}% - } -\AtEndDocument{% - \writelastpage - \if@final - \clearemptydoublepage - \else - \clearpage - \fi} - -\newcounter{section} -\newcounter{subsection}[section] -\newcounter{subsubsection}[subsection] -\newcounter{paragraph}[subsubsection] -\newcounter{subparagraph}[paragraph] -\newcounter{figure} -\newcounter{table} - -\newenvironment{tablenotes}{\if@traditional\fontsize{7.5bp}{9.5bp}\selectfont\else% -\if@contemporary\if@large\fontsize{6.5bp}{8.5bp}\selectfont\else\fontsize{7bp}{9bp}\selectfont\fi -\else\if@modern\fontsize{7.5bp}{9}\selectfont\else\fi\fi\fi% -\list{}{\setlength{\labelsep}{0pt}% -\setlength{\labelwidth}{0pt}% -\setlength{\leftmargin}{0pt}% -\setlength{\rightmargin}{0pt}% -\if@contemporary\if@large\setlength{\topsep}{1pt}\else\setlength{\topsep}{-6.5pt}\fi\setlength{\itemsep}{0pt}\fi% -\if@traditional\setlength{\itemsep}{0pt}\setlength{\topsep}{3pt}\else% -\if@modern\setlength{\itemsep}{2pt}\setlength{\topsep}{-6pt}\fi\fi% -\setlength{\partopsep}{0pt}% -\setlength{\listparindent}{0em}% -\setlength{\parsep}{0pt}}% -\item\relax% -}{\endlist\addvspace{0pt}}% - -\newcommand\thepage{\arabic{page}} -\renewcommand\thesection{\arabic{section}} -\renewcommand\thesubsection{{\thesection.\arabic{subsection}}} -\renewcommand\thesubsubsection{{\thesubsection.\arabic{subsubsection}}} -\renewcommand\theparagraph{\thesubsubsection.\arabic{paragraph}} -\renewcommand\thesubparagraph{\theparagraph.\arabic{subparagraph}} -\renewcommand\theequation{\arabic{equation}} - -\newcommand\contentsname{Contents} -\newcommand\listfigurename{List of Figures} -\newcommand\listtablename{List of Tables} -\newcommand\partname{Part} -\newcommand\appendixname{Appendix} -\newcommand\abstractname{Abstract} -\newcommand\keywordsname{Key words:} -\newcommand\refname{References} -\newcommand\bibname{References} -\newcommand\indexname{Index} -\newcommand\figurename{Figure} -\newcommand\tablename{Table} - -\newcommand{\clearemptydoublepage}{\newpage{\pagestyle{empty}\cleardoublepage}} - -\newif\if@mainmatter \@mainmattertrue - -\newcommand\frontmatter{% - \clearpage - \@mainmatterfalse - \pagenumbering{roman}} - -\newcommand\mainmatter{% - \clearpage - \@mainmattertrue - \pagenumbering{arabic}} - -\newcommand\backmatter{% - \clearpage - \@mainmatterfalse} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TITLE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\newlength{\dropfromtop} -\setlength{\dropfromtop}{\z@} - -\newif\if@appnotes -\newcommand{\application}{% - \global\@appnotestrue} - -\long\def\title{\@ifnextchar[{\short@title}{\@@title}} -\def\short@title[#1]{\titlemark{#1}\@@@title} -\def\@@title#1{\authormark{#1}\@@@title{#1}} -\long\def\@@@title#1{\gdef\@title{#1}} -\def\@subtitle{} -\long\def\subtitle#1{\gdef\@subtitle{#1}} -%\subtitle{Genome analysis} - -\newcounter{myauthcount} -\setcounter{myauthcount}{0} -\regtotcounter{myauthcount} - -\def\authorandsep{\ifnum\arabic{myauthcount@totc}=\arabic{myauthcount}\ifnum\arabic{myauthcount@totc}=1\else\if@traditional\if@small\ \else \fi\else \fi and \fi\else\fi} -\def\authorcommasep{\ifnum\arabic{myauthcount@totc}=\arabic{myauthcount}\else\ifnum\arabic{myauthcount@totc}=200\ifnum\arabic{myauthcount}<199,\else\fi\else\ifnum\arabic{myauthcount@totc}=199\ifnum\arabic{myauthcount}<198,\else\fi\else\ifnum\arabic{myauthcount@totc}=198\ifnum\arabic{myauthcount}<197,\else\fi\else\ifnum\arabic{myauthcount@totc}=197\ifnum\arabic{myauthcount}<196,\else\fi\else\ifnum\arabic{myauthcount@totc}=196\ifnum\arabic{myauthcount}<195,\else\fi\else\ifnum\arabic{myauthcount@totc}=195\ifnum\arabic{myauthcount}<194,\else\fi\else\ifnum\arabic{myauthcount@totc}=194\ifnum\arabic{myauthcount}<193,\else\fi\else\ifnum\arabic{myauthcount@totc}=193\ifnum\arabic{myauthcount}<192,\else\fi\else\ifnum\arabic{myauthcount@totc}=192\ifnum\arabic{myauthcount}<191,\else\fi\else\ifnum\arabic{myauthcount@totc}=191\ifnum\arabic{myauthcount}<190,\else\fi\else\ifnum\arabic{myauthcount@totc}=190\ifnum\arabic{myauthcount}<189,\else\fi\else\ifnum\arabic{myauthcount@totc}=189\ifnum\arabic{myauthcount}<188,\else\fi\else\ifnum\arabic{myauthcount@totc}=188\ifnum\arabic{myauthcount}<187,\else\fi\else\ifnum\arabic{myauthcount@totc}=187\ifnum\arabic{myauthcount}<186,\else\fi\else\ifnum\arabic{myauthcount@totc}=186\ifnum\arabic{myauthcount}<185,\else\fi\else\ifnum\arabic{myauthcount@totc}=185\ifnum\arabic{myauthcount}<184,\else\fi\else\ifnum\arabic{myauthcount@totc}=184\ifnum\arabic{myauthcount}<183,\else\fi\else\ifnum\arabic{myauthcount@totc}=183\ifnum\arabic{myauthcount}<182,\else\fi\else\ifnum\arabic{myauthcount@totc}=182\ifnum\arabic{myauthcount}<181,\else\fi\else\ifnum\arabic{myauthcount@totc}=181\ifnum\arabic{myauthcount}<180,\else\fi\else\ifnum\arabic{myauthcount@totc}=180\ifnum\arabic{myauthcount}<179,\else\fi\else\ifnum\arabic{myauthcount@totc}=179\ifnum\arabic{myauthcount}<178,\else\fi\else\ifnum\arabic{myauthcount@totc}=178\ifnum\arabic{myauthcount}<177,\else\fi\else\ifnum\arabic{myauthcount@totc}=177\ifnum\arabic{myauthcount}<176,\else\fi\else\ifnum\arabic{myauthcount@totc}=176\ifnum\arabic{myauthcount}<175,\else\fi\else\ifnum\arabic{myauthcount@totc}=175\ifnum\arabic{myauthcount}<174,\else\fi\else\ifnum\arabic{myauthcount@totc}=174\ifnum\arabic{myauthcount}<173,\else\fi\else\ifnum\arabic{myauthcount@totc}=173\ifnum\arabic{myauthcount}<172,\else\fi\else\ifnum\arabic{myauthcount@totc}=172\ifnum\arabic{myauthcount}<171,\else\fi\else\ifnum\arabic{myauthcount@totc}=171\ifnum\arabic{myauthcount}<170,\else\fi\else\ifnum\arabic{myauthcount@totc}=170\ifnum\arabic{myauthcount}<169,\else\fi\else\ifnum\arabic{myauthcount@totc}=169\ifnum\arabic{myauthcount}<168,\else\fi\else\ifnum\arabic{myauthcount@totc}=168\ifnum\arabic{myauthcount}<167,\else\fi\else\ifnum\arabic{myauthcount@totc}=167\ifnum\arabic{myauthcount}<166,\else\fi\else\ifnum\arabic{myauthcount@totc}=166\ifnum\arabic{myauthcount}<165,\else\fi\else\ifnum\arabic{myauthcount@totc}=165\ifnum\arabic{myauthcount}<164,\else\fi\else\ifnum\arabic{myauthcount@totc}=164\ifnum\arabic{myauthcount}<163,\else\fi\else\ifnum\arabic{myauthcount@totc}=163\ifnum\arabic{myauthcount}<162,\else\fi\else\ifnum\arabic{myauthcount@totc}=162\ifnum\arabic{myauthcount}<161,\else\fi\else\ifnum\arabic{myauthcount@totc}=161\ifnum\arabic{myauthcount}<160,\else\fi\else\ifnum\arabic{myauthcount@totc}=160\ifnum\arabic{myauthcount}<159,\else\fi\else\ifnum\arabic{myauthcount@totc}=159\ifnum\arabic{myauthcount}<158,\else\fi\else\ifnum\arabic{myauthcount@totc}=158\ifnum\arabic{myauthcount}<157,\else\fi\else\ifnum\arabic{myauthcount@totc}=157\ifnum\arabic{myauthcount}<156,\else\fi\else\ifnum\arabic{myauthcount@totc}=156\ifnum\arabic{myauthcount}<155,\else\fi\else\ifnum\arabic{myauthcount@totc}=155\ifnum\arabic{myauthcount}<154,\else\fi\else\ifnum\arabic{myauthcount@totc}=154\ifnum\arabic{myauthcount}<153,\else\fi\else\ifnum\arabic{myauthcount@totc}=153\ifnum\arabic{myauthcount}<152,\else\fi\else\ifnum\arabic{myauthcount@totc}=152\ifnum\arabic{myauthcount}<151,\else\fi\else\ifnum\arabic{myauthcount@totc}=151\ifnum\arabic{myauthcount}<150,\else\fi\else\ifnum\arabic{myauthcount@totc}=150\ifnum\arabic{myauthcount}<149,\else\fi\else\ifnum\arabic{myauthcount@totc}=149\ifnum\arabic{myauthcount}<148,\else\fi\else\ifnum\arabic{myauthcount@totc}=148\ifnum\arabic{myauthcount}<147,\else\fi\else\ifnum\arabic{myauthcount@totc}=147\ifnum\arabic{myauthcount}<146,\else\fi\else\ifnum\arabic{myauthcount@totc}=146\ifnum\arabic{myauthcount}<145,\else\fi\else\ifnum\arabic{myauthcount@totc}=145\ifnum\arabic{myauthcount}<144,\else\fi\else\ifnum\arabic{myauthcount@totc}=144\ifnum\arabic{myauthcount}<143,\else\fi\else\ifnum\arabic{myauthcount@totc}=143\ifnum\arabic{myauthcount}<142,\else\fi\else\ifnum\arabic{myauthcount@totc}=142\ifnum\arabic{myauthcount}<141,\else\fi\else\ifnum\arabic{myauthcount@totc}=141\ifnum\arabic{myauthcount}<140,\else\fi\else\ifnum\arabic{myauthcount@totc}=140\ifnum\arabic{myauthcount}<139,\else\fi\else\ifnum\arabic{myauthcount@totc}=139\ifnum\arabic{myauthcount}<138,\else\fi\else\ifnum\arabic{myauthcount@totc}=138\ifnum\arabic{myauthcount}<137,\else\fi\else\ifnum\arabic{myauthcount@totc}=137\ifnum\arabic{myauthcount}<136,\else\fi\else\ifnum\arabic{myauthcount@totc}=136\ifnum\arabic{myauthcount}<135,\else\fi\else\ifnum\arabic{myauthcount@totc}=135\ifnum\arabic{myauthcount}<134,\else\fi\else\ifnum\arabic{myauthcount@totc}=134\ifnum\arabic{myauthcount}<133,\else\fi\else\ifnum\arabic{myauthcount@totc}=133\ifnum\arabic{myauthcount}<132,\else\fi\else\ifnum\arabic{myauthcount@totc}=132\ifnum\arabic{myauthcount}<131,\else\fi\else\ifnum\arabic{myauthcount@totc}=131\ifnum\arabic{myauthcount}<130,\else\fi\else\ifnum\arabic{myauthcount@totc}=130\ifnum\arabic{myauthcount}<129,\else\fi\else\ifnum\arabic{myauthcount@totc}=129\ifnum\arabic{myauthcount}<128,\else\fi\else\ifnum\arabic{myauthcount@totc}=128\ifnum\arabic{myauthcount}<127,\else\fi\else\ifnum\arabic{myauthcount@totc}=127\ifnum\arabic{myauthcount}<126,\else\fi\else\ifnum\arabic{myauthcount@totc}=126\ifnum\arabic{myauthcount}<125,\else\fi\else\ifnum\arabic{myauthcount@totc}=125\ifnum\arabic{myauthcount}<124,\else\fi\else\ifnum\arabic{myauthcount@totc}=124\ifnum\arabic{myauthcount}<123,\else\fi\else\ifnum\arabic{myauthcount@totc}=123\ifnum\arabic{myauthcount}<122,\else\fi\else\ifnum\arabic{myauthcount@totc}=122\ifnum\arabic{myauthcount}<121,\else\fi\else\ifnum\arabic{myauthcount@totc}=121\ifnum\arabic{myauthcount}<120,\else\fi\else\ifnum\arabic{myauthcount@totc}=120\ifnum\arabic{myauthcount}<119,\else\fi\else\ifnum\arabic{myauthcount@totc}=119\ifnum\arabic{myauthcount}<118,\else\fi\else\ifnum\arabic{myauthcount@totc}=118\ifnum\arabic{myauthcount}<117,\else\fi\else\ifnum\arabic{myauthcount@totc}=117\ifnum\arabic{myauthcount}<116,\else\fi\else\ifnum\arabic{myauthcount@totc}=116\ifnum\arabic{myauthcount}<115,\else\fi\else\ifnum\arabic{myauthcount@totc}=115\ifnum\arabic{myauthcount}<114,\else\fi\else\ifnum\arabic{myauthcount@totc}=114\ifnum\arabic{myauthcount}<113,\else\fi\else\ifnum\arabic{myauthcount@totc}=113\ifnum\arabic{myauthcount}<112,\else\fi\else\ifnum\arabic{myauthcount@totc}=112\ifnum\arabic{myauthcount}<111,\else\fi\else\ifnum\arabic{myauthcount@totc}=111\ifnum\arabic{myauthcount}<110,\else\fi\else\ifnum\arabic{myauthcount@totc}=110\ifnum\arabic{myauthcount}<109,\else\fi\else\ifnum\arabic{myauthcount@totc}=109\ifnum\arabic{myauthcount}<108,\else\fi\else\ifnum\arabic{myauthcount@totc}=108\ifnum\arabic{myauthcount}<107,\else\fi\else\ifnum\arabic{myauthcount@totc}=107\ifnum\arabic{myauthcount}<106,\else\fi\else\ifnum\arabic{myauthcount@totc}=106\ifnum\arabic{myauthcount}<105,\else\fi\else\ifnum\arabic{myauthcount@totc}=105\ifnum\arabic{myauthcount}<104,\else\fi\else\ifnum\arabic{myauthcount@totc}=104\ifnum\arabic{myauthcount}<103,\else\fi\else\ifnum\arabic{myauthcount@totc}=103\ifnum\arabic{myauthcount}<102,\else\fi\else\ifnum\arabic{myauthcount@totc}=102\ifnum\arabic{myauthcount}<101,\else\fi\else\ifnum\arabic{myauthcount@totc}=101\ifnum\arabic{myauthcount}<100,\else\fi\else\ifnum\arabic{myauthcount@totc}=100\ifnum\arabic{myauthcount}<99,\else\fi\else\ifnum\arabic{myauthcount@totc}=99\ifnum\arabic{myauthcount}<98,\else\fi\else\ifnum\arabic{myauthcount@totc}=98\ifnum\arabic{myauthcount}<97,\else\fi\else\ifnum\arabic{myauthcount@totc}=97\ifnum\arabic{myauthcount}<96,\else\fi\else\ifnum\arabic{myauthcount@totc}=96\ifnum\arabic{myauthcount}<95,\else\fi\else\ifnum\arabic{myauthcount@totc}=95\ifnum\arabic{myauthcount}<94,\else\fi\else\ifnum\arabic{myauthcount@totc}=94\ifnum\arabic{myauthcount}<93,\else\fi\else\ifnum\arabic{myauthcount@totc}=93\ifnum\arabic{myauthcount}<92,\else\fi\else\ifnum\arabic{myauthcount@totc}=92\ifnum\arabic{myauthcount}<91,\else\fi\else\ifnum\arabic{myauthcount@totc}=91\ifnum\arabic{myauthcount}<90,\else\fi\else\ifnum\arabic{myauthcount@totc}=90\ifnum\arabic{myauthcount}<89,\else\fi\else\ifnum\arabic{myauthcount@totc}=89\ifnum\arabic{myauthcount}<88,\else\fi\else\ifnum\arabic{myauthcount@totc}=88\ifnum\arabic{myauthcount}<87,\else\fi\else\ifnum\arabic{myauthcount@totc}=87\ifnum\arabic{myauthcount}<86,\else\fi\else\ifnum\arabic{myauthcount@totc}=86\ifnum\arabic{myauthcount}<85,\else\fi\else\ifnum\arabic{myauthcount@totc}=85\ifnum\arabic{myauthcount}<84,\else\fi\else\ifnum\arabic{myauthcount@totc}=84\ifnum\arabic{myauthcount}<83,\else\fi\else\ifnum\arabic{myauthcount@totc}=83\ifnum\arabic{myauthcount}<82,\else\fi\else\ifnum\arabic{myauthcount@totc}=82\ifnum\arabic{myauthcount}<81,\else\fi\else\ifnum\arabic{myauthcount@totc}=81\ifnum\arabic{myauthcount}<80,\else\fi\else\ifnum\arabic{myauthcount@totc}=80\ifnum\arabic{myauthcount}<79,\else\fi\else\ifnum\arabic{myauthcount@totc}=79\ifnum\arabic{myauthcount}<78,\else\fi\else\ifnum\arabic{myauthcount@totc}=78\ifnum\arabic{myauthcount}<77,\else\fi\else\ifnum\arabic{myauthcount@totc}=77\ifnum\arabic{myauthcount}<76,\else\fi\else\ifnum\arabic{myauthcount@totc}=76\ifnum\arabic{myauthcount}<75,\else\fi\else\ifnum\arabic{myauthcount@totc}=75\ifnum\arabic{myauthcount}<74,\else\fi\else\ifnum\arabic{myauthcount@totc}=74\ifnum\arabic{myauthcount}<73,\else\fi\else\ifnum\arabic{myauthcount@totc}=73\ifnum\arabic{myauthcount}<72,\else\fi\else\ifnum\arabic{myauthcount@totc}=72\ifnum\arabic{myauthcount}<71,\else\fi\else\ifnum\arabic{myauthcount@totc}=71\ifnum\arabic{myauthcount}<70,\else\fi\else\ifnum\arabic{myauthcount@totc}=70\ifnum\arabic{myauthcount}<69,\else\fi\else\ifnum\arabic{myauthcount@totc}=69\ifnum\arabic{myauthcount}<68,\else\fi\else\ifnum\arabic{myauthcount@totc}=68\ifnum\arabic{myauthcount}<67,\else\fi\else\ifnum\arabic{myauthcount@totc}=67\ifnum\arabic{myauthcount}<66,\else\fi\else\ifnum\arabic{myauthcount@totc}=66\ifnum\arabic{myauthcount}<65,\else\fi\else\ifnum\arabic{myauthcount@totc}=65\ifnum\arabic{myauthcount}<64,\else\fi\else\ifnum\arabic{myauthcount@totc}=64\ifnum\arabic{myauthcount}<63,\else\fi\else\ifnum\arabic{myauthcount@totc}=63\ifnum\arabic{myauthcount}<62,\else\fi\else\ifnum\arabic{myauthcount@totc}=62\ifnum\arabic{myauthcount}<61,\else\fi\else\ifnum\arabic{myauthcount@totc}=61\ifnum\arabic{myauthcount}<60,\else\fi\else\ifnum\arabic{myauthcount@totc}=60\ifnum\arabic{myauthcount}<59,\else\fi\else\ifnum\arabic{myauthcount@totc}=59\ifnum\arabic{myauthcount}<58,\else\fi\else\ifnum\arabic{myauthcount@totc}=58\ifnum\arabic{myauthcount}<57,\else\fi\else\ifnum\arabic{myauthcount@totc}=57\ifnum\arabic{myauthcount}<56,\else\fi\else\ifnum\arabic{myauthcount@totc}=56\ifnum\arabic{myauthcount}<55,\else\fi\else\ifnum\arabic{myauthcount@totc}=55\ifnum\arabic{myauthcount}<54,\else\fi\else\ifnum\arabic{myauthcount@totc}=54\ifnum\arabic{myauthcount}<53,\else\fi\else\ifnum\arabic{myauthcount@totc}=53\ifnum\arabic{myauthcount}<52,\else\fi\else\ifnum\arabic{myauthcount@totc}=52\ifnum\arabic{myauthcount}<51,\else\fi\else\ifnum\arabic{myauthcount@totc}=51\ifnum\arabic{myauthcount}<50,\else\fi\else\ifnum\arabic{myauthcount@totc}=50\ifnum\arabic{myauthcount}<49,\else\fi\else\ifnum\arabic{myauthcount@totc}=49\ifnum\arabic{myauthcount}<48,\else\fi\else\ifnum\arabic{myauthcount@totc}=48\ifnum\arabic{myauthcount}<47,\else\fi\else\ifnum\arabic{myauthcount@totc}=47\ifnum\arabic{myauthcount}<46,\else\fi\else\ifnum\arabic{myauthcount@totc}=46\ifnum\arabic{myauthcount}<45,\else\fi\else\ifnum\arabic{myauthcount@totc}=45\ifnum\arabic{myauthcount}<44,\else\fi\else\ifnum\arabic{myauthcount@totc}=44\ifnum\arabic{myauthcount}<43,\else\fi\else\ifnum\arabic{myauthcount@totc}=43\ifnum\arabic{myauthcount}<42,\else\fi\else\ifnum\arabic{myauthcount@totc}=42\ifnum\arabic{myauthcount}<41,\else\fi\else\ifnum\arabic{myauthcount@totc}=41\ifnum\arabic{myauthcount}<40,\else\fi\else\ifnum\arabic{myauthcount@totc}=40\ifnum\arabic{myauthcount}<39,\else\fi\else\ifnum\arabic{myauthcount@totc}=39\ifnum\arabic{myauthcount}<38,\else\fi\else\ifnum\arabic{myauthcount@totc}=38\ifnum\arabic{myauthcount}<37,\else\fi\else\ifnum\arabic{myauthcount@totc}=37\ifnum\arabic{myauthcount}<36,\else\fi\else\ifnum\arabic{myauthcount@totc}=36\ifnum\arabic{myauthcount}<35,\else\fi\else\ifnum\arabic{myauthcount@totc}=35\ifnum\arabic{myauthcount}<34,\else\fi\else\ifnum\arabic{myauthcount@totc}=34\ifnum\arabic{myauthcount}<33,\else\fi\else\ifnum\arabic{myauthcount@totc}=33\ifnum\arabic{myauthcount}<32,\else\fi\else\ifnum\arabic{myauthcount@totc}=32\ifnum\arabic{myauthcount}<31,\else\fi\else\ifnum\arabic{myauthcount@totc}=31\ifnum\arabic{myauthcount}<30,\else\fi\else\ifnum\arabic{myauthcount@totc}=30\ifnum\arabic{myauthcount}<29,\else\fi\else\ifnum\arabic{myauthcount@totc}=29\ifnum\arabic{myauthcount}<28,\else\fi\else\ifnum\arabic{myauthcount@totc}=28\ifnum\arabic{myauthcount}<27,\else\fi\else\ifnum\arabic{myauthcount@totc}=27\ifnum\arabic{myauthcount}<26,\else\fi\else\ifnum\arabic{myauthcount@totc}=26\ifnum\arabic{myauthcount}<25,\else\fi\else\ifnum\arabic{myauthcount@totc}=25\ifnum\arabic{myauthcount}<24,\else\fi\else\ifnum\arabic{myauthcount@totc}=24\ifnum\arabic{myauthcount}<23,\else\fi\else\ifnum\arabic{myauthcount@totc}=23\ifnum\arabic{myauthcount}<22,\else\fi\else\ifnum\arabic{myauthcount@totc}=22\ifnum\arabic{myauthcount}<21,\else\fi\else\ifnum\arabic{myauthcount@totc}=21\ifnum\arabic{myauthcount}<20,\else\fi\else\ifnum\arabic{myauthcount@totc}=20\ifnum\arabic{myauthcount}<19,\else\fi\else\ifnum\arabic{myauthcount@totc}=19\ifnum\arabic{myauthcount}<18,\else\fi\else\ifnum\arabic{myauthcount@totc}=18\ifnum\arabic{myauthcount}<17,\else\fi\else\ifnum\arabic{myauthcount@totc}=17\ifnum\arabic{myauthcount}<16,\else\fi\else\ifnum\arabic{myauthcount@totc}=16\ifnum\arabic{myauthcount}<15,\else\fi\else\ifnum\arabic{myauthcount@totc}=15\ifnum\arabic{myauthcount}<14,\else\fi\else\ifnum\arabic{myauthcount@totc}=14\ifnum\arabic{myauthcount}<13,\else\fi\else\ifnum\arabic{myauthcount@totc}=13\ifnum\arabic{myauthcount}<12,\else\fi\else\ifnum\arabic{myauthcount@totc}=12\ifnum\arabic{myauthcount}<11,\else\fi\else\ifnum\arabic{myauthcount@totc}=11\ifnum\arabic{myauthcount}<10,\else\fi\else\ifnum\arabic{myauthcount@totc}=10\ifnum\arabic{myauthcount}<9,\else\fi\else\ifnum\arabic{myauthcount@totc}=9\ifnum\arabic{myauthcount}<8,\else\fi\else\ifnum\arabic{myauthcount@totc}=8\ifnum\arabic{myauthcount}<7,\else\fi\else\ifnum\arabic{myauthcount@totc}=7\ifnum\arabic{myauthcount}<6,\else\fi\else\ifnum\arabic{myauthcount@totc}=6\ifnum\arabic{myauthcount}<5,\else\fi\else\ifnum\arabic{myauthcount@totc}=5\ifnum\arabic{myauthcount}<4,\else\fi\else\ifnum\arabic{myauthcount@totc}=4\ifnum\arabic{myauthcount}<3,\else\fi\else\ifnum\arabic{myauthcount@totc}=3\ifnum\arabic{myauthcount}<2,\else\fi\else\ifnum\arabic{myauthcount@totc}=2\else,\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} - -\def\author{\@@author}% -\newcommand{\@@author}[2][]{% - \g@addto@macro\@author{% - \refstepcounter{myauthcount}% - \hspace*{0.001pt}\hbox{\authorandsep#2\authorcommasep\ifx#1\@empty\else\textsuperscript{#1}\fi}\space} - }% - -\def\@vol{} -\def\vol#1{\global\def\@vol{#1}} -\def\@issue{} -\def\issue#1{\global\def\@issue{#1}} - -\newcommand\defcase[1]{\@namedef{mycase@\the\numexpr#1\relax}} -\newcommand\myswitch[1]{\@nameuse{mycase@\the\numexpr#1\relax}} - -\defcase{0}{Month} -\defcase{1}{January} -\defcase{2}{February} -\defcase{3}{March} -\defcase{4}{April} -\defcase{5}{May} -\defcase{6}{June} -\defcase{7}{July} -\defcase{8}{August} -\defcase{9}{September} -\defcase{10}{October} -\defcase{11}{November} -\defcase{12}{December} - -\def\@history{} -\def\history#1{\global\def\@history{#1}} -\history{} - -\newcounter{myhistorycount} -\setcounter{myhistorycount}{0} -\regtotcounter{myhistorycount} - -\def\historycommasep{\ifnum\arabic{myhistorycount@totc}=\arabic{myhistorycount}\else.\ \fi} - -\def\received#1#2#3{\g@addto@macro\@history{\refstepcounter{myhistorycount}\textbf{Received:} #1\ \myswitch{#2}\ #3\historycommasep}} -\def\revised#1#2#3{\g@addto@macro\@history{\refstepcounter{myhistorycount} \textbf{Revised received:} #1\ \myswitch{#2}\ #3\historycommasep}} -\def\accepted#1#2#3{\g@addto@macro\@history{\refstepcounter{myhistorycount} \textbf{Accepted:} #1\ \myswitch{#2}\ #3\historycommasep}} - -\def\abstract#1{\let\subheading\abstsubheading\global\def\@abstract{#1}} -\def\subheading#1{#1} - -\def\abstsubheading#1{% -\if@modern -\par\vskip3pt\vskip3.5pt{\fontseries{b}\fontshape{it}\selectfont#1}\enspace% -\else -\if@traditional% - \par\vskip6.5pt{\itshape#1}\enspace% -\else% -\if@contemporary% - \par\vskip3pt{\sffamily\fontseries{b}\selectfont\itshape#1}\enspace% -\fi\fi\fi} - -\def\@keywords{} - -\newcommand\keywords[2][Keywords]{\g@addto@macro\@keywords{% -\if@modern% - \vskip6pt% -\else% -\if@traditional% - \vskip6.5pt% -\else% - \vskip3pt -\fi% -\fi\par% -\if@modern% - {{\sffamilyfont\fontseries{b}\fontsize{8.5bp}{11}#1}\enspace\fontseries{m}\selectfont#2}% -\else% -\if@traditional - {\fontsize{8.5bp}{11bp}\selectfont\textbf{#1:}\ #2\par}% -\else - {\sffamily\fontseries{b}\selectfont\itshape #1}\enspace#2% -\fi\fi -}}% - -\def\@nonabstract{} - -\newcommand\otherabstract[2][Other abstract heading]{\g@addto@macro\@nonabstract{% -\if@contemporary% - \vskip14pt% - {\fontseries{b}\fontsize{12bp}{14bp}\selectfont\centering#1\par}% - \vskip3pt% - \noindent{{\sffamily\fontseries{b}\fontsize{8bp}{11bp}\selectfont#2\par}}% -\else -\if@traditional - \vskip4mm% - {\fontsize{11.5bp}{13.5bp}\fontseries{b}\selectfont\centering #1\par}\vskip5pt% - \noindent{\fontsize{9bp}{11.75bp}\selectfont#2\par}% -\else -\if@modern% - \vskip11pt - {\sffamily\bfseries\fontsize{13bp}{15}\selectfont#1\par\vskip6pt} - \noindent{{\sffamily\fontseries{b}\fontsize{9.5bp}{11.5bp}\selectfont#2\par}}% -\fi% -\fi% -\fi% -} -} - -\def\@editor{} -\def\editor#1{\global\def\@editor{#1}} -\def\pubyear#1{\global\def\@pubyear{#1}} -\def\copyrightyear#1{\global\def\@copyrightyear{#1}} -\def\journaltitle#1{\global\def\@journaltitle{#1}} -\def\volume#1{\global\def\@volume{#1}}\volume{} -\def\issue#1{\global\def\@issue{#1}}\issue{} - -\def\@boxedtext{} -\def\boxedtext{\@@boxedtext}% -\newcommand{\@@boxedtext}[2]{\def\@boxedtext{\ifx#2\@empty\else\removelastskip\vskip2pt\nointerlineskip\fbox{\parbox{.97\textwidth}{\textbf{#1}\par#2\vspace{-8pt}}}\par\nointerlineskip\vskip8pt\nointerlineskip\fi}} - -\def\orgdiv#1{#1} -\def\orgname#1{#1} -\def\orgaddress#1{#1} -\def\street#1{#1} -\def\postcode#1{#1} -\def\state#1{#1} -\def\country#1{#1} - -\newcounter{myaddcount} -\setcounter{myaddcount}{0} -\regtotcounter{myaddcount} - -\def\addressandsep{\ifnum\arabic{myaddcount@totc}=\arabic{myaddcount}\ifnum\arabic{myaddcount@totc}=1\else\ and \fi\else\fi} -\def\addresscommasep{\ifnum\arabic{myaddcount@totc}=\arabic{myaddcount}\else\ifnum\arabic{myaddcount@totc}=200\ifnum\arabic{myaddcount}<199, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=199\ifnum\arabic{myaddcount}<198, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=198\ifnum\arabic{myaddcount}<197, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=197\ifnum\arabic{myaddcount}<196, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=196\ifnum\arabic{myaddcount}<195, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=195\ifnum\arabic{myaddcount}<194, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=194\ifnum\arabic{myaddcount}<193, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=193\ifnum\arabic{myaddcount}<192, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=192\ifnum\arabic{myaddcount}<191, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=191\ifnum\arabic{myaddcount}<190, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=190\ifnum\arabic{myaddcount}<189, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=189\ifnum\arabic{myaddcount}<188, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=188\ifnum\arabic{myaddcount}<187, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=187\ifnum\arabic{myaddcount}<186, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=186\ifnum\arabic{myaddcount}<185, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=185\ifnum\arabic{myaddcount}<184, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=184\ifnum\arabic{myaddcount}<183, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=183\ifnum\arabic{myaddcount}<182, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=182\ifnum\arabic{myaddcount}<181, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=181\ifnum\arabic{myaddcount}<180, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=180\ifnum\arabic{myaddcount}<179, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=179\ifnum\arabic{myaddcount}<178, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=178\ifnum\arabic{myaddcount}<177, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=177\ifnum\arabic{myaddcount}<176, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=176\ifnum\arabic{myaddcount}<175, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=175\ifnum\arabic{myaddcount}<174, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=174\ifnum\arabic{myaddcount}<173, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=173\ifnum\arabic{myaddcount}<172, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=172\ifnum\arabic{myaddcount}<171, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=171\ifnum\arabic{myaddcount}<170, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=170\ifnum\arabic{myaddcount}<169, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=169\ifnum\arabic{myaddcount}<168, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=168\ifnum\arabic{myaddcount}<167, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=167\ifnum\arabic{myaddcount}<166, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=166\ifnum\arabic{myaddcount}<165, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=165\ifnum\arabic{myaddcount}<164, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=164\ifnum\arabic{myaddcount}<163, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=163\ifnum\arabic{myaddcount}<162, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=162\ifnum\arabic{myaddcount}<161, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=161\ifnum\arabic{myaddcount}<160, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=160\ifnum\arabic{myaddcount}<159, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=159\ifnum\arabic{myaddcount}<158, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=158\ifnum\arabic{myaddcount}<157, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=157\ifnum\arabic{myaddcount}<156, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=156\ifnum\arabic{myaddcount}<155, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=155\ifnum\arabic{myaddcount}<154, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=154\ifnum\arabic{myaddcount}<153, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=153\ifnum\arabic{myaddcount}<152, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=152\ifnum\arabic{myaddcount}<151, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=151\ifnum\arabic{myaddcount}<150, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=150\ifnum\arabic{myaddcount}<149, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=149\ifnum\arabic{myaddcount}<148, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=148\ifnum\arabic{myaddcount}<147, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=147\ifnum\arabic{myaddcount}<146, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=146\ifnum\arabic{myaddcount}<145, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=145\ifnum\arabic{myaddcount}<144, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=144\ifnum\arabic{myaddcount}<143, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=143\ifnum\arabic{myaddcount}<142, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=142\ifnum\arabic{myaddcount}<141, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=141\ifnum\arabic{myaddcount}<140, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=140\ifnum\arabic{myaddcount}<139, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=139\ifnum\arabic{myaddcount}<138, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=138\ifnum\arabic{myaddcount}<137, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=137\ifnum\arabic{myaddcount}<136, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=136\ifnum\arabic{myaddcount}<135, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=135\ifnum\arabic{myaddcount}<134, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=134\ifnum\arabic{myaddcount}<133, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=133\ifnum\arabic{myaddcount}<132, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=132\ifnum\arabic{myaddcount}<131, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=131\ifnum\arabic{myaddcount}<130, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=130\ifnum\arabic{myaddcount}<129, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=129\ifnum\arabic{myaddcount}<128, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=128\ifnum\arabic{myaddcount}<127, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=127\ifnum\arabic{myaddcount}<126, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=126\ifnum\arabic{myaddcount}<125, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=125\ifnum\arabic{myaddcount}<124, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=124\ifnum\arabic{myaddcount}<123, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=123\ifnum\arabic{myaddcount}<122, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=122\ifnum\arabic{myaddcount}<121, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=121\ifnum\arabic{myaddcount}<120, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=120\ifnum\arabic{myaddcount}<119, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=119\ifnum\arabic{myaddcount}<118, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=118\ifnum\arabic{myaddcount}<117, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=117\ifnum\arabic{myaddcount}<116, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=116\ifnum\arabic{myaddcount}<115, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=115\ifnum\arabic{myaddcount}<114, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=114\ifnum\arabic{myaddcount}<113, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=113\ifnum\arabic{myaddcount}<112, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=112\ifnum\arabic{myaddcount}<111, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=111\ifnum\arabic{myaddcount}<110, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=110\ifnum\arabic{myaddcount}<109, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=109\ifnum\arabic{myaddcount}<108, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=108\ifnum\arabic{myaddcount}<107, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=107\ifnum\arabic{myaddcount}<106, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=106\ifnum\arabic{myaddcount}<105, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=105\ifnum\arabic{myaddcount}<104, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=104\ifnum\arabic{myaddcount}<103, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=103\ifnum\arabic{myaddcount}<102, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=102\ifnum\arabic{myaddcount}<101, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=101\ifnum\arabic{myaddcount}<100, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=100\ifnum\arabic{myaddcount}<99, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=99\ifnum\arabic{myaddcount}<98, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=98\ifnum\arabic{myaddcount}<97, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=97\ifnum\arabic{myaddcount}<96, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=96\ifnum\arabic{myaddcount}<95, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=95\ifnum\arabic{myaddcount}<94, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=94\ifnum\arabic{myaddcount}<93, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=93\ifnum\arabic{myaddcount}<92, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=92\ifnum\arabic{myaddcount}<91, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=91\ifnum\arabic{myaddcount}<90, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=90\ifnum\arabic{myaddcount}<89, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=89\ifnum\arabic{myaddcount}<88, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=88\ifnum\arabic{myaddcount}<87, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=87\ifnum\arabic{myaddcount}<86, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=86\ifnum\arabic{myaddcount}<85, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=85\ifnum\arabic{myaddcount}<84, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=84\ifnum\arabic{myaddcount}<83, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=83\ifnum\arabic{myaddcount}<82, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=82\ifnum\arabic{myaddcount}<81, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=81\ifnum\arabic{myaddcount}<80, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=80\ifnum\arabic{myaddcount}<79, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=79\ifnum\arabic{myaddcount}<78, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=78\ifnum\arabic{myaddcount}<77, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=77\ifnum\arabic{myaddcount}<76, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=76\ifnum\arabic{myaddcount}<75, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=75\ifnum\arabic{myaddcount}<74, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=74\ifnum\arabic{myaddcount}<73, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=73\ifnum\arabic{myaddcount}<72, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=72\ifnum\arabic{myaddcount}<71, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=71\ifnum\arabic{myaddcount}<70, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=70\ifnum\arabic{myaddcount}<69, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=69\ifnum\arabic{myaddcount}<68, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=68\ifnum\arabic{myaddcount}<67, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=67\ifnum\arabic{myaddcount}<66, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=66\ifnum\arabic{myaddcount}<65, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=65\ifnum\arabic{myaddcount}<64, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=64\ifnum\arabic{myaddcount}<63, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=63\ifnum\arabic{myaddcount}<62, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=62\ifnum\arabic{myaddcount}<61, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=61\ifnum\arabic{myaddcount}<60, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=60\ifnum\arabic{myaddcount}<59, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=59\ifnum\arabic{myaddcount}<58, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=58\ifnum\arabic{myaddcount}<57, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=57\ifnum\arabic{myaddcount}<56, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=56\ifnum\arabic{myaddcount}<55, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=55\ifnum\arabic{myaddcount}<54, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=54\ifnum\arabic{myaddcount}<53, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=53\ifnum\arabic{myaddcount}<52, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=52\ifnum\arabic{myaddcount}<51, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=51\ifnum\arabic{myaddcount}<50, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=50\ifnum\arabic{myaddcount}<49, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=49\ifnum\arabic{myaddcount}<48, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=48\ifnum\arabic{myaddcount}<47, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=47\ifnum\arabic{myaddcount}<46, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=46\ifnum\arabic{myaddcount}<45, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=45\ifnum\arabic{myaddcount}<44, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=44\ifnum\arabic{myaddcount}<43, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=43\ifnum\arabic{myaddcount}<42, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=42\ifnum\arabic{myaddcount}<41, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=41\ifnum\arabic{myaddcount}<40, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=40\ifnum\arabic{myaddcount}<39, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=39\ifnum\arabic{myaddcount}<38, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=38\ifnum\arabic{myaddcount}<37, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=37\ifnum\arabic{myaddcount}<36, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=36\ifnum\arabic{myaddcount}<35, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=35\ifnum\arabic{myaddcount}<34, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=34\ifnum\arabic{myaddcount}<33, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=33\ifnum\arabic{myaddcount}<32, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=32\ifnum\arabic{myaddcount}<31, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=31\ifnum\arabic{myaddcount}<30, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=30\ifnum\arabic{myaddcount}<29, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=29\ifnum\arabic{myaddcount}<28, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=28\ifnum\arabic{myaddcount}<27, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=27\ifnum\arabic{myaddcount}<26, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=26\ifnum\arabic{myaddcount}<25, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=25\ifnum\arabic{myaddcount}<24, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=24\ifnum\arabic{myaddcount}<23, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=23\ifnum\arabic{myaddcount}<22, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=22\ifnum\arabic{myaddcount}<21, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=21\ifnum\arabic{myaddcount}<20, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=20\ifnum\arabic{myaddcount}<19, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=19\ifnum\arabic{myaddcount}<18, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=18\ifnum\arabic{myaddcount}<17, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=17\ifnum\arabic{myaddcount}<16, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=16\ifnum\arabic{myaddcount}<15, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=15\ifnum\arabic{myaddcount}<14, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=14\ifnum\arabic{myaddcount}<13, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=13\ifnum\arabic{myaddcount}<12, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=12\ifnum\arabic{myaddcount}<11, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=11\ifnum\arabic{myaddcount}<10, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=10\ifnum\arabic{myaddcount}<9, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=9\ifnum\arabic{myaddcount}<8, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=8\ifnum\arabic{myaddcount}<7, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=7\ifnum\arabic{myaddcount}<6, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=6\ifnum\arabic{myaddcount}<5, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=5\ifnum\arabic{myaddcount}<4, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=4\ifnum\arabic{myaddcount}<3, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=3\ifnum\arabic{myaddcount}<2, \else\unskip\fi\else\ifnum\arabic{myaddcount@totc}=2\else,\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} - -\def\@address{} -\def\address{\@@address}% -\newcommand{\@@address}[2][]{% - \g@addto@macro\@address{% - \refstepcounter{myaddcount}% - \if@modern% - \ifnum\arabic{myaddcount}>1\par\fi% - \ifx#1\@empty\else\textsuperscript{#1}\fi#2% - \else% - \if@contemporary% - \ifnum\arabic{myaddcount}>1\par\vskip2pt\fi% - \ifx#1\@empty\else\textsuperscript{#1}\fi#2% - \else% - \if@traditional - \ifx#1\@empty\else\textsuperscript{#1}\fi#2\par% - \else - \addressandsep\ifx#1\@empty\else\textsuperscript{#1}\fi#2\addresscommasep% - \fi% - \fi% - \fi}% - }% -\def\@corresp{} -\def\corresp{\@@corresp}% -\newcommand{\@@corresp}[2][]{% - \g@addto@macro\@corresp{% - \ifx#1\@empty\else\textsuperscript{#1}\fi#2}}% - -\def\@DOI{} -\def\DOI#1{\global\def\@DOI{#1}} - -\definecolor{gray}{cmyk}{0, 0, 0, 0.15} -\definecolor{grayfifty}{cmyk}{0, 0, 0, 0.5} -\definecolor{graysixtyfive}{cmyk}{0, 0, 0, 0.65} -\definecolor{graysevtyfive}{cmyk}{0, 0, 0, 0.75} -\newlength{\extraspace} -\setlength{\extraspace}{\if@traditional-12pt\else\if@modern\if@otherarticle-9pt\else\if@small10pt\else12pt\fi\fi\else\z@\fi\fi} - -\newcount\currentpageno% - -\newcommand\maketitle{\par - \begingroup -\global\currentpageno\c@page% -\def\@latex@warning@no@line##1{}% - \renewcommand\thefootnote{\@fnsymbol\c@footnote}% - \def\@makefnmark{\rlap{\@textsuperscript{\normalfont\@thefnmark}}}% - \long\def\@makefntext##1{\parindent 3mm\noindent\@textsuperscript{\normalfont\@thefnmark}##1}% - \if@twocolumn - \ifnum \col@number=\@ne - \@maketitle - \else - \twocolumn[\thispagestyle{opening}\@maketitle]%% - \enlargethispage{-12pt}% - \fi - \else - \newpage% - \thispagestyle{opening}% - \global\@topnum\z@% - \@maketitle - \enlargethispage{-12pt}% - \fi% - \@thanks% - \endgroup - \setcounter{footnote}{0}% - \global\let\thanks\relax - \global\let\maketitle\relax -% \global\let\@maketitle\relax -% \global\let\@address\@empty -% \global\let\@corresp\@empty -% \global\let\@history\@empty - \global\let\@editor\@empty - \global\let\@thanks\@empty - \global\let\@author\@empty - \global\let\@date\@empty - \global\let\@subtitle\@empty - \global\let\@title\@empty - \global\let\@boxedtext\@empty - %\global\let\@pubyear\@empty - \global\let\address\relax - \global\let\boxedtext\relax - \global\let\history\relax - \global\let\editor\relax - \global\let\title\relax - \global\let\author\relax - \global\let\date\relax - %\global\let\pubyear\relax - \global\let\@copyrightline\@empty - \global\let\and\relax - \@afterindentfalse\@afterheading -} - -\newlength{\aboveskipchk}% -\setlength{\aboveskipchk}{\z@}% - -\def\@access{} -\def\access#1{\gdef\@access{#1}} - -\def\appnotes#1{\gdef\@appnotes{#1}} -\appnotes{} - -\def\specialissue#1{\gdef\@specialissue{#1}} -\specialissue{} - -\newlength{\titlepagewd} -\if@modern - \if@large - \setlength{\titlepagewd}{\textwidth} - \else - \if@medium - \setlength{\titlepagewd}{36.5pc} - \else - \if@small - \setlength{\titlepagewd}{29pc} - \else - \setlength{\titlepagewd}{36.5pc} - \fi - \fi - \fi -\else -\if@traditional - \setlength{\titlepagewd}{\textwidth} -\else - \setlength{\titlepagewd}{\textwidth} -\fi -\fi - -\definecolor{jnlclr}{cmyk}{.80,.29,.05,0} -\definecolor{jnlruleclr}{cmyk}{.45,.06,.05,0} - -\def\@maketitle{% - \let\footnote\thanks% - \clearemptydoublepage%% - \if@modern\vspace*{13pt}% - \if@large\noindent\textcolor{black!60}{\rule{\textwidth}{1\p@}}\else% - \if@medium\noindent\textcolor{black!60}{\rule{\textwidth}{1\p@}}\else% - \if@small\noindent\textcolor{black!60}{\rule{\textwidth}{1\p@}}\else% - \fi\fi\fi% - \else% - \if@traditional - \if@large\vspace*{9.6mm}\noindent\rule{\textwidth}{.5\p@}\vspace*{2pt}\else% - \if@medium\vspace*{4mm}\noindent\rule{\textwidth}{.5\p@}\vspace*{2pt}\else% - \null\vspace*{4mm}\nointerlineskip\noindent\rule{\textwidth}{.5\p@}% - \fi\fi - \else - \if@contemporary - \if@large\null\vspace*{40bp}\nointerlineskip\else% - \if@medium\if@mediumone\null\vspace*{64bp}\nointerlineskip\else\null\vspace*{39.5bp}\nointerlineskip\fi\else% - \if@small\null\vspace*{50bp}\nointerlineskip\else% - \fi\fi\fi% - \else - \fi - \fi\fi\par% - % - \if@contemporary\sffamilyfont\else\if@traditional\else\sffamilyfont\fi\fi% - {% - %%\parbox[t]{\titlepagewd}{% - \if@modern\vspace*{5pt}\else\if@traditional\else\fi\fi% - \ifx\@subtitle\@empty% - \else% - \if@traditional\else{\sffamilyfontcn\if@modern\if@small\fontsize{12bp}{19}\selectfont\else\fontsize{14bp}{21}\selectfont\fi\else\fontsize{14bp}{21}\selectfont\fi\raggedright \@subtitle \par}% - \vspace{7.5\p@}\fi% - \fi% - % Title here - \if@modern% - {\sffamilyfontbold\if@small\fontsize{20bp}{21}\selectfont\raggedright\else\fontsize{20bp}{21}\selectfont\fi\raggedright \@title \par}% - \else% - \if@traditional% - \vskip8bp% - {\if@large\fontsize{21bp}{22bp}\bfseries\selectfont\else\if@medium\fontsize{21bp}{22bp}\bfseries\selectfont\else\fontsize{21bp}{22bp}\selectfont\bfseries\fi\fi\centering\@title\par}%% - \else% - \if@contemporary - %{\fontsize{9.8bp}{10}\selectfont \MakeUppercase{\@appnotes}}\\[6.5pt] - {\if@large\fontsize{20bp}{24bp}\selectfont\bfseries\else\if@medium\fontsize{20bp}{24bp}\selectfont\bfseries\else\fontsize{20bp}{24bp}\selectfont\bfseries\fi\fi\centering\@title\par}%% - \else - {\if@large\fontsize{24bp}{26}\bfseries\selectfont\else\if@medium\fontsize{24bp}{25}\selectfont\bfseries\else\fontsize{18bp}{20}\selectfont\bfseries\fi\fi\leftskip0pt plus1fill\rightskip0pt plus1fill \@title \par}%% - \fi% - \fi% - \fi% - % Title below space - \if@modern\if@medium\vspace{8.8\p@}\else\if@small\vspace{7.8\p@}\else\vspace{6\p@}\fi\fi\else% - \if@traditional\if@large\vskip11.5pt\else\if@medium\vskip11.5pt\else\vskip11.5pt\fi\fi% - \else% - \if@contemporary\if@large\vspace{10\p@}\else\if@medium\vspace{10\p@}\else\vspace{10\p@}\fi\fi\else - \fi% - \fi% - \fi% - % Author here - \if@modern - {\reset@font\sffamilyfont\fontseries{b}\if@small\fontsize{11bp}{15}\selectfont\else\fontsize{11bp}{15}\selectfont\fi\raggedright \@author \par} - \if@otherarticle\else\noindent\vspace*{.5pt}\textcolor{black!60}{\rule{\textwidth}{1\p@}}\par\fi% - \else - \if@traditional% - {\if@large\fontsize{11bp}{15bp}\selectfont\else\if@medium\fontsize{11bp}{15bp}\selectfont\else\fontsize{11bp}{15bp}\selectfont\fi\fi\centering\@author\par}% - \else - \if@contemporary - {\fontseries{m}\selectfont\if@large\fontsize{12bp}{14bp}\selectfont\else\if@medium\fontsize{12bp}{14bp}\selectfont\else\fontsize{12bp}{14bp}\selectfont\fi\fi\centering\@author\par}% - \else% - \fi% - \fi% - \fi% - % Author below space - \if@modern\if@medium\vspace{3.5\p@}\else\if@small\vspace{3.5\p@}\else\vspace{3.5\p@}\fi\fi\else - \if@traditional% - \if@large\vskip8pt\else\if@medium\vskip8pt\else\vskip8pt\fi\fi% - \else - \if@contemporary\vskip20\p@\else% - \fi\fi\fi% - % Address here - \if@modern% - {\reset@font\sffamily\fontseries{m}\if@large\fontsize{8bp}{11}\rightskip24pt\else\if@medium\fontsize{8bp}{11}\if@mediumone\raggedright\else\rightskip10pt\fi\else\if@small\fontsize{8bp}{11}\raggedright\else\fi\fi\fi\selectfont \@address \par}% - \else% - \if@traditional% - {\if@large\fontsize{9bp}{11.5bp}\else\if@medium\fontsize{9bp}{11.5bp}\else\fontsize{9bp}{11.5bp}\fi\fi\selectfont\centering\@address\par}% - \else% - \if@contemporary% - {\sffamily\if@large\fontsize{7bp}{9bp}\else\if@medium\fontsize{7bp}{9bp}\else\if@small\fontsize{7bp}{9bp}\else\fi\fi\fi\selectfont\centering\@address\par}% - \else% - \fi% - \fi% - \fi% - % Address below space - \if@modern\vspace{0\p@}\else\if@traditional\vskip5.75pt\else\vskip9pt\fi\fi - % Correspondence author here - \if@modern - {\sffamily\fontseries{m}\if@large\fontsize{8bp}{11}\selectfont\else\if@medium\fontsize{8bp}{11}\selectfont\else\if@small\fontsize{8bp}{11}\selectfont\else\fontsize{8.5bp}{12}\selectfont\fi\fi\fi\raggedright \@corresp \par}% - \else - \if@traditional% - {\if@large\fontsize{7bp}{10}\selectfont\else\if@medium\fontsize{7bp}{10}\selectfont\else\fontsize{7bp}{10}\selectfont\fi\fi\centering\@corresp\par}% - \else - \if@contemporary% - {\sffamily\fontseries{m}\fontsize{7bp}{9bp}\selectfont\centering\@corresp\par}% - \else - \fi - \fi - \fi - % Correspondence below space - \if@modern\vspace{2\p@}\else\fi% - % Editor here - \if@modern% - {\sffamilyfont\if@large\fontsize{8.5bp}{12}\selectfont\else\if@medium\fontsize{8bp}{11.5}\selectfont\else\if@small\fontsize{8.5bp}{12}\selectfont\else\fontsize{8.5bp}{12}\selectfont\fi\fi\fi\raggedright \@editor \par}% - \else% - \fi% - % Editor below space -%% \if@modern\vspace{4\p@}\else\if@contemporary\vskip6pt\else\fi\fi% -%% % History here -%% \if@modern% -%% {\sffamilyfont\fontsize{7}{12}\selectfont\raggedright \@history \par}% -%% \else% -%% \if@traditional% -%% {\fontsize{8bp}{10}\selectfont\leftskip0pt plus1fill\rightskip0pt plus1fill \@history \par}% -%% \else% -%% \if@contemporary% -%% {\sffamily\fontsize{7bp}{9bp}\selectfont\centering\@history\par}% -%% \else -%% {\fontsize{8bp}{10}\selectfont\leftskip0pt plus1fill\rightskip0pt plus1fill \@history \par} -%% \fi -%% \fi -%% \fi -%% % History below space - \if@modern\if@small\vspace{15\p@}\else\vspace{20\p@}\fi\else - \if@traditional\if@large\vskip14pt\else\if@medium\vskip14pt\else\vskip14pt\fi\fi\else - \if@contemporary\if@large\vspace*{7pt}\else\if@medium\vspace*{7pt}\else\vspace*{11pt}\fi\fi\else\fi\fi\fi - % Abstract here - \if@modern% - \if@mediumone\raggedright\else\if@small\raggedright\else\fi\fi\ifx\@abstract\@empty\vskip-20pt\else{\let\section\absection{\sffamilyfont\if@traditional\fontsize{11.5bp}{13.5bp}\else\if@contemporary\fontsize{12bp}{14bp}\else\if@modern\fontsize{13bp}{14bp}\else\fontsize{10bp}{12}\fi\fi\fi\bfseries\selectfont Abstract}\par} - \vskip5pt\fi% - \else - \if@traditional% - \if@large - {\fontsize{11.5bp}{13.5bp}\fontseries{b}\selectfont\centering Abstract\par}\vskip5pt% - \else% - \if@medium% - {\fontsize{11.5bp}{13.5bp}\fontseries{b}\selectfont\centering Abstract\par}\vskip5pt% - \else - {\fontsize{11.5bp}{13.5bp}\fontseries{b}\selectfont\centering Abstract\par}\vskip5pt% - \fi - \fi - \else - \if@contemporary - \if@large - \noindent{\color{grayfifty}\dhorline{\textwidth}{2pt}}\par\vskip3pt% - {\fontseries{b}\fontsize{12bp}{14bp}\selectfont\centering Abstract\par}% - \vskip3pt% - \else - \if@medium - \noindent{\color{grayfifty}\dhorline{\textwidth}{2pt}}\par\vskip3pt% - {\fontseries{b}\fontsize{13bp}{14bp}\selectfont\centering Abstract\par}% - \vskip3pt% - \else\noindent{\color{grayfifty}\dhorline{\textwidth}{2pt}}\par\vskip3pt% - {\fontseries{b}\fontsize{12bp}{14bp}\selectfont\centering Abstract\par}% - \vskip3pt% - \fi - \fi - \else - \fi - \fi - \fi - \begingroup - \if@modern - \if@small\begin{minipage}[t]{\textwidth}\else\begin{minipage}[t]{\textwidth}\fi - \else - \if@traditional% - \else - \if@contemporary - %\if@large\begin{minipage}[t]{\textwidth}\else\begin{minipage}[t]{\textwidth}\fi - \else - \fi\fi\fi\parindent=0pt - {\if@modern - \sffamilyfont - \if@large\sffamily\fontseries{b}\fontsize{9.5bp}{11.5}\selectfont\else - \if@medium\sffamily\fontseries{b}\fontsize{9.5bp}{11.5}\else - \if@small\sffamily\fontseries{b}\fontsize{9.5bp}{11.5}\selectfont\else - \fontsize{9bp}{12}\selectfont - \fi\fi\fi - \else - \if@traditional% - \if@large\fontsize{9.75bp}{12.5bp}\selectfont\bfseries\else% - \if@medium\fontsize{9.75bp}{12.5bp}\selectfont\bfseries\else% - \fontsize{9.75bp}{12.5bp}\selectfont\bfseries\fi\fi - \else - \if@contemporary - \sffamily\fontseries{b}\fontsize{8bp}{11bp}\selectfont - \else - \fi - \fi - \fi - \if@modern - \if@medium\rightskip-12pt\else\fi - \else - \if@traditional% - \if@large\else\fi - \else - \fi - \fi - \@abstract}% - \ifx\@keywords\@empty\else\@keywords\par\fi% - \ifx\@nonabstract\@empty\else\@nonabstract\par\fi% - \ifx\@boxedtext\@empty\else\vspace*{12pt}\par\fi% - \@boxedtext% - \if@contemporary - \noindent{\color{grayfifty}\dhorline{\textwidth}{2pt}}\par% - \if@large - \vspace*{12pt}\par% \vspace*{6.5pt}\par{\color{jnlruleclr}\rule{542pt}{2pt}} - \else - \if@medium - \vspace*{6.5pt}\par%{\color{jnlruleclr}\rule{484pt}{2pt}} - \else - \vspace*{11pt}\par%{\color{jnlruleclr}\rule{390pt}{2pt}} - \fi - \fi - \else\if@traditional\par\vskip20pt\else\par\fi\fi - \if@contemporary\else\if@modern\end{minipage}\fi\fi - \endgroup% - }% - \vspace{12\p@ plus 6\p@ minus 6\p@}% - \vspace{\extraspace} - }% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%%%%%%%%%%%%%%%%%%%%%%%%%%%% Abstract %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\newcommand{\absection}[1]{% - \par\noindent{\bfseries #1}\space\ignorespaces} - -% Section macros - -% Lowest level heading that takes a number by default -\setcounter{secnumdepth}{3} - -\renewcommand{\@seccntformat}[1]{\if@unnumsec\else\csname the#1\endcsname.\space\fi} - -\def\secsize{% - \if@modern% - \if@large\sffamily\fontsize{15.5bp}{17}\selectfont\bfseries\else% - \if@medium\sffamily\fontsize{15.5bp}{17}\selectfont\bfseries\else% - \if@small\sffamily\fontsize{15.5bp}{17}\selectfont\bfseries\else% - \sffamilyfontbold\fontsize{10bp}{12}\selectfont\fi\fi\fi% - \else%% - \if@traditional% - \fontsize{11.5bp}{11.75bp}\fontseries{b}\selectfont\centering% - \else% - \if@contemporary - \sffamily\fontsize{10.5bp}{14bp}\selectfont\bfseries\centering% - \else - \fi - \fi% - \fi} - -\def\subsecsize{% - \if@modern% - \sffamilyfont\fontsize{13bp}{17}\selectfont\bfseries - \else%% - \if@traditional% - \fontsize{10.5bp}{11.75bp}\fontseries{b}\selectfont\centering% - \else% - \if@contemporary - \sffamily\fontsize{10bp}{12bp}\selectfont\centering - \else\fi\fi% - \fi} - -\def\subsubsecsize{% - \if@modern% - \sffamilyfont\fontsize{10.5bp}{11.5}\selectfont\bfseries% - \else%% - \if@traditional% - \fontsize{9.75bp}{11.75bp}\selectfont\bfseries\itshape\centering% - \else% - \if@contemporary - \sffamily\fontsize{9.5bp}{12}\selectfont\itshape\centering% - \else\fi\fi% - \fi} - -\def\paragraphsize{% - \if@modern% - \if@large\sffamilyfont\fontsize{9bp}{11.5}\selectfont\itshape\else\sffamilyfont\fontsize{9bp}{11.5}\selectfont\itshape\fi%% - \else%% - \if@traditional% - \fontsize{9.5bp}{11.75bp}\selectfont\bfseries\centering - \else% - \if@contemporary - \fontsize{8.5bp}{11.5}\selectfont\bfseries\centering - \else\fi\fi% - \fi} - -\def\subparagraphsize{% - \if@modern% - \if@large\sffamilyfont\fontsize{9bp}{11.5}\selectfont\else\sffamilyfont\fontsize{9bp}{11.5}\selectfont\fi% - \else%% - \if@traditional% - \fontsize{9.5bp}{11.75bp}\selectfont\centering - \else% - \if@contemporary - \fontseries{m}\fontsize{8.25bp}{11}\selectfont\centering - \else\fi\fi% - \fi} - -\def\section{% - \@startsection{section}{1}{\z@} - {\if@modern-10\p@ plus -3\p@\else\if@contemporary-15\p@ plus -3\p@\else\if@traditional-15\p@ plus -3\p@\else-15\p@ plus -3\p@\fi\fi\fi} - {\if@modern\if@large6pt\else6pt\fi\else\if@traditional5\p@\else\if@contemporary6\p@\else4\p@\fi\fi\fi} - {\reset@font\if@modern\raggedright\fi\secsize}} - -\def\subsection{% - \@startsection{subsection}{2}{\z@} - {\if@modern-8\p@ plus -3\p@\else\if@contemporary-9\p@ plus -2\p@\else\if@traditional-12\p@ plus -2\p@\else-11\p@ plus -2\p@\fi\fi\fi} - {\if@modern\if@large6pt\else6pt\fi\else\if@traditional3\p@\else\if@contemporary6\p@\else2\p@\fi\fi\fi} - {\reset@font\if@modern\raggedright\fi\subsecsize}} - -\def\subsubsection{% - \@startsection{subsubsection}{3}{\z@} - {\if@modern-6\p@ plus -3\p@\else\if@contemporary-6\p@ plus -1\p@\else\if@traditional-6\p@ plus -1\p@\else-11\p@ plus -1\p@\fi\fi\fi} - {\if@modern\if@large2pt\else2pt\fi\else\if@contemporary3\p@\else\if@traditional1.5\p@\else0.1em\fi\fi\fi} - {\reset@font\if@modern\raggedright\fi\subsubsecsize}} - -\def\textcolon{\text{\rm :}} - - \def\paragraph{% - \@startsection{paragraph}{4}{\z@} - {\if@modern-6\p@\else\if@contemporary-6\p@ plus -1\p@\else\if@traditional-12\p@ plus -2\p@\else-11\p@ plus -1\p@\fi\fi\fi} - {\if@modern\if@large1pt\else1pt\fi\else\if@contemporary3\p@\else\if@traditional1\p@\else0.1em\fi\fi\fi} - {\reset@font\paragraphsize}} - -\def\subparagraph{\@startsection{subparagraph}{5}{\z@}% - {\if@modern-6\p@\else\if@contemporary-3.5\p@ plus -1\p@\else\if@traditional-12\p@ plus -2\p@\else-3.5\p@ plus -1\p@\fi\fi\fi}% - {\if@modern\if@large1pt\else1pt\fi\else\if@contemporary3\p@\else\if@traditional1\p@\else0.1em\fi\fi\fi}% - {\subparagraphsize}} - -\def\@startsection#1#2#3#4#5#6{% - \if@noskipsec \leavevmode \fi - \par - \@tempskipa #4\relax - \@afterindenttrue - \ifdim \@tempskipa <\z@ - \@tempskipa -\@tempskipa \@afterindentfalse - \fi - \if@nobreak - \everypar{}% - \if@traditional - \ifnum#2=2\vspace*{-3pt}\fi\ifnum#2=3\vspace*{-1pt}\fi\ifnum#2=4\vspace*{-1pt}\fi\ifnum#2=5\vspace*{-1pt}\fi% - \else% - \if@contemporary\ifnum#2=2\vspace*{-3pt}\fi\ifnum#2=3\vspace*{-3pt}\fi\ifnum#2=5\vspace*{0.5pt}\fi% - \else% - \fi% - \fi%% - \else - \addpenalty\@secpenalty\addvspace\@tempskipa - \fi - \@ifstar - {\@ssect{#3}{#4}{#5}{#6}}% - {\@dblarg{\@sect{#1}{#2}{#3}{#4}{#5}{#6}}}} - -\def\@sect#1#2#3#4#5#6[#7]#8{% - \ifnum #2>\c@secnumdepth - \let\@svsec\@empty - \else - \refstepcounter{#1}% - \protected@edef\@svsec{\@seccntformat{#1}\relax}% - \fi - \@tempskipa #5\relax - \ifdim \@tempskipa>\z@ - \begingroup - #6{% - \@hangfrom{\hskip #3\relax\@svsec}% - \interlinepenalty \@M #8\@@par}% - \endgroup - \csname #1mark\endcsname{#7}% - \addcontentsline{toc}{#1}{% - \ifnum #2>\c@secnumdepth \else - \protect\numberline{\csname the#1\endcsname}% - \fi - #7}% - \else - \def\@svsechd{% - #6{\hskip #3\relax - \@svsec #8}% - \csname #1mark\endcsname{#7}% - \addcontentsline{toc}{#1}{% - \ifnum #2>\c@secnumdepth \else - \protect\numberline{\csname the#1\endcsname}% - \fi - #7}}% - \fi - \@xsect{#5}} - - -% ******************** -% Figures and tables * -% ******************** - -% Table and array parameters -\setlength\arraycolsep{.5em} -\setlength\tabcolsep{.5em} -\setlength\arrayrulewidth{.5pt} -\setlength\doublerulesep{2.5pt} -\setlength\extrarowheight{\z@} -\renewcommand\arraystretch{1} - -%\newlength{\abovecaptionskip} -%\newlength{\belowcaptionskip} -\if@contemporary - \if@large - \setlength{\abovecaptionskip}{11pt} - \setlength{\belowcaptionskip}{5.5pt} - \else - \setlength{\abovecaptionskip}{11pt} - \setlength{\belowcaptionskip}{4.5pt} - \fi -\if@traditional - \setlength{\abovecaptionskip}{11pt} - \setlength{\belowcaptionskip}{5.5pt} -\else - \setlength{\abovecaptionskip}{13pt} - \setlength{\belowcaptionskip}{2pt} -\fi -\fi - -\long\def\@makecaption#1#2{\if@modern\vskip3pt\else\vspace{\abovecaptionskip}\fi% - \begingroup - \if@modern\else\scriptsize\fi\sffamily - \text{\sfb #1.}\space{#2}\par - \endgroup} - -\long\def\@tablecaption#1#2{% - \begingroup% - \if@modern% - \fontsize{9bp}{11.5pt}\sffamily\selectfont% - \textbf{#1.}\space{#2\strut\par}% - \else% - \if@traditional% - \fontsize{8bp}{11bp}\selectfont\textbf{#1}\space#2\strut\par% - \else% - \if@contemporary - \if@large% - \sffamily\fontsize{8bp}{9.5bp}\selectfont% - \textbf{#1}\space{#2\strut\par}% - \else% - \if@medium - \sffamily\fontsize{8bp}{11.5bp}\selectfont% - \textbf{#1}\space{#2\strut\par}% - \else - \sffamily\fontsize{8bp}{11bp}\selectfont% - \textbf{#1}\space{#2\strut\par}% - \fi% - \fi% - \fi - \fi% - \fi% - \endgroup\vspace{\belowcaptionskip}} - -\long\def\@figurecaption#1#2{\if@traditional\vspace*{7.5pt}\else\if@modern\else\vspace{\abovecaptionskip}\fi\fi% - \begingroup% - \if@modern% -{\vskip3pt% - \fontsize{8bp}{11}\selectfont% - %\fontsize{7.5pt}{10.5pt}\sffamily\selectfont% - \textbf{#1}\space#2\strut\par}% - \else% - \if@traditional% - \if@boxenvpresent\fontsize{8bp}{11bp}\selectfont\else\fontsize{8bp}{11bp}\selectfont\fi\raggedright\textbf{#1}\space#2\strut\par% - \else% - \if@boxenvpresent\sffamily\fontseries{m}\fontsize{7bp}{10pt}\selectfont\else\sffamily\fontsize{7.5bp}{9.5pt}\selectfont\fi% - \textbf{#1}\space{#2\strut\par}% - \fi% - \fi% - \endgroup\if@modern\vspace{\belowcaptionskip}\fi} - -\if@traditional\if@large\def\arraystretch{1}\else\def\arraystretch{1}\fi\fi - -% Table rules -\if@modern - \def\toprule{\noalign{\ifnum0=`}\fi% - \hrule \@height 0.5pt - \hrule \@height 4pt \@width 0pt \futurelet \@tempa\@xhline} - \def\midrule{\noalign{\ifnum0=`}\fi% - \hrule \@height 3pt \@width 0pt% - \hrule \@height 0.5pt% - \hrule \@height 4pt \@width 0pt \futurelet \@tempa\@xhline} - \def\botrule{\noalign{\ifnum0=`}\fi% - \hrule \@height 3.75pt \@width 0pt - \hrule \@height 0.5pt \futurelet \@tempa\@xhline} -\else -\if@traditional - \def\toprule{\noalign{\ifnum0=`}\fi% - \hrule \@height 0.25pt - \hrule \@height 4pt \@width 0pt \futurelet \@tempa\@xhline} - \def\midrule{\noalign{\ifnum0=`}\fi% - \hrule \@height 3.5pt \@width 0pt% - \hrule \@height 0.25pt% - \hrule \@height 8pt \@width 0pt \futurelet \@tempa\@xhline} - \def\botrule{\noalign{\ifnum0=`}\fi% - \hrule \@height 6pt \@width 0pt - \hrule \@height 0.25pt \futurelet \@tempa\@xhline} - \setlength\arrayrulewidth{0.25pt} -\else - \def\toprule{\noalign{\ifnum0=`}\fi% - \hrule \@height 0.5pt - \hrule \@height 4pt \@width 0pt \futurelet \@tempa\@xhline} - \def\midrule{\noalign{\ifnum0=`}\fi% - \hrule \@height 3pt \@width 0pt% - \hrule \@height 0.5pt% - \hrule \@height 4pt \@width 0pt \futurelet \@tempa\@xhline} - \def\botrule{\noalign{\ifnum0=`}\fi% - \hrule \@height 3.75pt \@width 0pt - \hrule \@height 0.5pt \futurelet \@tempa\@xhline} - \def\cline#1{\@cline#1\@nil\noalign{\vskip3pt}} -\fi -\fi -\def\hrulefill{\leavevmode\leaders\hrule height .5pt\hfill\kern\z@} - -\def\thefigure{\@arabic\c@figure} -\def\fps@figure{tbp} -\def\ftype@figure{1} -\def\ext@figure{lof} -\def\fnum@figure{\figurename~\thefigure} -\def\figure{\let\@makecaption\@figurecaption\@float{figure}} -\let\endfigure\end@float -\newcommand\figalttext[2][]{\relax}% -\@namedef{figure*}{\let\@makecaption\@figurecaption\@dblfloat{figure}} -\@namedef{endfigure*}{\end@dblfloat} - -\def\sidewaysfigure{\let\@makecaption\@figurecaption\@rotfloat{figure}} -\let\endsidewaysfigure\end@rotfloat - -\renewenvironment{sidewaysfigure*} - {\let\@makecaption\@figurecaption\@rotdblfloat{figure}} - {\end@rotdblfloat} - - - -\def\thetable{\@arabic\c@table} -\def\fps@table{tbp} -\def\ftype@table{2} -\def\ext@table{lot} -\def\fnum@table{\tablename~\thetable} - -\if@contemporary - \usepackage{arydshln} - \setlength{\dashlinedash}{0.5pt} - \setlength\dashlinegap{1.5pt} - \if@large - \def \@floatboxreset {% - \reset@font - \sffamily\fontsize{7.5bp}{9.5bp}\selectfont - \@setminipage - } - \else - \def \@floatboxreset {% - \reset@font - \sffamily\fontsize{7bp}{10bp}\selectfont - \@setminipage - } - \fi -\fi - -\def\table{% -\if@contemporary% - \if@large% - \def\arraystretch{1} - \def\toprule{\hdashline\noalign{\vspace*{3.5pt}}}% - \def\midrule{\noalign{\vspace*{2.5pt}}\hdashline\noalign{\vspace*{5pt}}}% - \def\botrule{\noalign{\vspace*{3.5pt}}\hdashline} - \else - \if@medium% - \def\arraystretch{1.2} - \def\toprule{\hdashline\noalign{\vspace*{1.5pt}}}% - \def\midrule{\noalign{\vspace*{1.5pt}}\hdashline\noalign{\vspace*{6pt}}}% - \def\botrule{\noalign{\vspace*{2.5pt}}\hdashline} - \else% - \def\toprule{\hdashline\noalign{\vspace*{1.5pt}}}% - \def\midrule{\noalign{\vspace*{1.5pt}}\hdashline\noalign{\vspace*{5.5pt}}}% - \def\botrule{\noalign{\vspace*{1.5pt}}\hdashline}\def\arraystretch{1.1}% - \fi% - \fi% -\fi% -\let\@makecaption\@tablecaption\let\source\tablesource\@float{table}} -\def\endtable{\end@float} - -\@namedef{table*}{\if@contemporary% - \if@large% - \def\arraystretch{1} - \def\toprule{\hdashline\noalign{\vspace*{3.5pt}}}% - \def\midrule{\noalign{\vspace*{2.5pt}}\hdashline\noalign{\vspace*{5pt}}}% - \def\botrule{\noalign{\vspace*{3.5pt}}\hdashline} - \else - \if@medium% - \def\arraystretch{1.2} - \def\toprule{\hdashline\noalign{\vspace*{1.5pt}}}% - \def\midrule{\noalign{\vspace*{1.5pt}}\hdashline\noalign{\vspace*{6pt}}}% - \def\botrule{\noalign{\vspace*{2.5pt}}\hdashline} - \else% - \def\toprule{\hdashline\noalign{\vspace*{1.5pt}}}% - \def\midrule{\noalign{\vspace*{1.5pt}}\hdashline\noalign{\vspace*{5.5pt}}}% - \def\botrule{\noalign{\vspace*{1.5pt}}\hdashline}\def\arraystretch{1.1}% - \fi% - \fi% -\fi%% -\let\@makecaption\@tablecaption\@dblfloat{table}} -\@namedef{endtable*}{\end@dblfloat} - -\def\sidewaystable{\if@contemporary% - \if@large% - \def\arraystretch{1} - \def\toprule{\hdashline\noalign{\vspace*{3.5pt}}}% - \def\midrule{\noalign{\vspace*{2.5pt}}\hdashline\noalign{\vspace*{5pt}}}% - \def\botrule{\noalign{\vspace*{3.5pt}}\hdashline} - \else - \if@medium% - \def\arraystretch{1.2} - \def\toprule{\hdashline\noalign{\vspace*{1.5pt}}}% - \def\midrule{\noalign{\vspace*{1.5pt}}\hdashline\noalign{\vspace*{6pt}}}% - \def\botrule{\noalign{\vspace*{2.5pt}}\hdashline} - \else% - \def\toprule{\hdashline\noalign{\vspace*{1.5pt}}}% - \def\midrule{\noalign{\vspace*{1.5pt}}\hdashline\noalign{\vspace*{5.5pt}}}% - \def\botrule{\noalign{\vspace*{1.5pt}}\hdashline}\def\arraystretch{1.1}% - \fi% - \fi% -\fi% -\let\@makecaption\@tablecaption\@rotfloat{table}} - -\let\endsidewaystable\end@rotfloat - -\renewenvironment{sidewaystable*} - {\if@contemporary% - \if@large% - \def\arraystretch{1} - \def\toprule{\hdashline\noalign{\vspace*{3.5pt}}}% - \def\midrule{\noalign{\vspace*{2.5pt}}\hdashline\noalign{\vspace*{5pt}}}% - \def\botrule{\noalign{\vspace*{3.5pt}}\hdashline} - \else - \if@medium% - \def\arraystretch{1.2} - \def\toprule{\hdashline\noalign{\vspace*{1.5pt}}}% - \def\midrule{\noalign{\vspace*{1.5pt}}\hdashline\noalign{\vspace*{6pt}}}% - \def\botrule{\noalign{\vspace*{2.5pt}}\hdashline} - \else% - \def\toprule{\hdashline\noalign{\vspace*{1.5pt}}}% - \def\midrule{\noalign{\vspace*{1.5pt}}\hdashline\noalign{\vspace*{5.5pt}}}% - \def\botrule{\noalign{\vspace*{1.5pt}}\hdashline}\def\arraystretch{1.1}% - \fi% - \fi% -\fi% -\let\@makecaption\@tablecaption\@rotdblfloat{table}} - {\end@rotdblfloat} - - -\newif\if@rotate \@rotatefalse -\newif\if@rotatecenter \@rotatecenterfalse -\def\rotatecenter{\global\@rotatecentertrue} -\def\rotateendcenter{\global\@rotatecenterfalse} -\def\rotate{\global\@rotatetrue} -\def\endrotate{\global\@rotatefalse} -\newdimen\rotdimen -\def\rotstart#1{\special{ps: gsave currentpoint currentpoint translate - #1 neg exch neg exch translate}} -\def\rotfinish{\special{ps: currentpoint grestore moveto}} -\def\rotl#1{\rotdimen=\ht#1\advance\rotdimen by \dp#1 - \hbox to \rotdimen{\vbox to\wd#1{\vskip \wd#1 - \rotstart{270 rotate}\box #1\vss}\hss}\rotfinish} -\def\rotr#1{\rotdimen=\ht #1\advance\rotdimen by \dp#1 - \hbox to \rotdimen{\vbox to \wd#1{\vskip \wd#1 - \rotstart{90 rotate}\box #1\vss}\hss}\rotfinish} - -\newdimen\tempdime -\newbox\temptbox - -% From ifmtarg.sty -% Copyright Peter Wilson and Donald Arseneau, 2000 -\begingroup -\catcode`\Q=3 -\long\gdef\@ifmtarg#1{\@xifmtarg#1QQ\@secondoftwo\@firstoftwo\@nil} -\long\gdef\@xifmtarg#1#2Q#3#4#5\@nil{#4} -\long\gdef\@ifnotmtarg#1{\@xifmtarg#1QQ\@firstofone\@gobble\@nil} -\endgroup - -\def\tablesize{\if@traditional% - \if@large\@setfontsize\tablesize{10\p@}{12.5\p@}\else - \@setfontsize\tablesize{8\p@}{10\p@}\fi\else\fi} - -\newenvironment{processtable}[3]{\setbox\temptbox=\hbox{{\tablesize #2}}% -\tempdime\wd\temptbox\@processtable{#1}{#2}{#3}{\tempdime}} -{\relax} - -\newcommand{\@processtable}[4]{% -\if@rotate -\setbox4=\vbox to \hsize{\vss\hbox to \textheight{% -\begin{minipage}{#4}% -\@ifmtarg{#1}{}{\caption{#1}}{\tablesize #2}% -\vskip7\p@\noindent -\parbox{#4}{\fontsize{7bp}{9}\selectfont #3\par}% -\end{minipage}}\vss}% -\rotr{4} -\else -\hbox to \hsize{\hss\begin{minipage}[t]{#4}% -\vskip2.9pt -\@ifmtarg{#1}{}{\caption{#1}}{\tablesize #2}% -\vskip6\p@\parindent=12pt -\parbox{#4}{\fontsize{7bp}{9}\selectfont #3\par}% -\end{minipage}\hss}\fi}% - -\newcolumntype{P}[1]{>{\raggedright\let\\\@arraycr\hangindent1em}p{#1}} - -% ****************************** -% List numbering and lettering * -% ****************************** -\def\labelenumi{{\arabic{enumi}.}} -\def\theenumi{\arabic{enumi}} -\def\labelenumii{{\alph{enumii}.}} -\def\theenumii{\alph{enumii}} -\def\p@enumii{\theenumi} -\def\labelenumiii{{\roman{enumiii}.}} -\def\theenumiii{\roman{enumiii}} -\def\p@enumiii{\theenumi(\theenumii)} -\def\labelenumiv{{(\arabic{enumiv})}} -\def\theenumiv{\Alph{enumiv}} -\def\p@enumiv{\p@enumiii\theenumiii} -\def\labelitemi{\if@modern{\small$\bullet$}\else{\textbullet}\fi} -\def\labelitemii{{--}} -\def\labelitemiii{\if@modern{\small$\bullet$}\else{\textbullet}\fi} -\def\labelitemiv{\if@modern{\small$\bullet$}\else{\textbullet}\fi} - -\def\@listI{\leftmargin\leftmargini \topsep\medskipamount} -\let\@listi\@listI -\@listi -\def\@listii{\topsep\z@\leftmargin\leftmarginii} -\def\@listiii{\leftmargin\leftmarginiii \topsep\z@} -\def\@listiv{\leftmargin\leftmarginiv \topsep\z@} -\def\@listv{\leftmargin\leftmarginv \topsep\z@} -\def\@listvi{\leftmargin\leftmarginvi \topsep\z@} - -\if@modern\setlength{\leftmargini}{3mm}\else\setlength{\leftmargini}{0mm}\fi -\setlength{\leftmarginii}{0mm} -\setlength{\leftmarginiii}{0mm} -\setlength{\leftmarginiv}{\z@} - -% Changes to the list parameters for enumerate -\def\enumargs{% - \partopsep \z@ - \if@contemporary - \if@large - \ifnum\@enumdepth >1\fontsize{7.75bp}{11bp}\selectfont\itemsep1\p@\else\fontsize{8bp}{11.5bp}\selectfont\itemsep0\p@\fi - \else - \if@medium\ifnum\@enumdepth >1\itemsep0.5\p@\else\itemsep2\p@\fi - \else\itemsep1\p@ - \fi - \fi - \else\itemsep\z@ - \fi% - \parsep \z@ - \labelsep \if@contemporary1em\else\if@traditional5.5pt\else\if@modern.5em\else1em\fi\fi\fi - \listparindent \parindent - \itemindent \z@ - \if@contemporary\if@medium\ifnum\@enumdepth >1\topsep 1.5\p@\else\topsep 6\p@\fi% - \else% - \ifnum\@enumdepth >1\topsep 2\p@\else\topsep 6.5\p@\fi - \fi - \else - \if@traditional - \ifnum\@enumdepth >1\topsep 0\p@\else\topsep 2mm\fi - \else\ifnum \@enumdepth>1\topsep 0\p@\else\topsep 7\p@\fi\fi\fi -} - -\def\enumerate{% - \@ifnextchar[{\@numerate}{\@numerate[0.]}} - -\def\@numerate[#1]{% - \ifnum \@enumdepth >3 \@toodeep\else - \advance\@enumdepth \@ne - \edef\@enumctr{enum\romannumeral\the\@enumdepth} - \list{\csname label\@enumctr\endcsname}{% - \enumargs - \setlength{\leftmargin}{\csname leftmargin\romannumeral\the\@enumdepth\endcsname} - \usecounter{\@enumctr} - \if@contemporary - \settowidth\labelwidth{\sffamily\selectfont#1} - \addtolength{\leftmargin}{\labelwidth} - \addtolength{\leftmargin}{\labelsep} - \def\makelabel##1{\hss \llap{\sffamily\selectfont##1}}% - \else - \if@traditional% - \ifnum\@enumdepth >1 - \settowidth\labelwidth{#1}% - \else - \setlength{\labelwidth}{5mm} - \fi - \addtolength{\leftmargin}{\labelwidth}% - \addtolength{\leftmargin}{\labelsep}% - \def\makelabel##1{\hss \llap{##1}}% - \else - \settowidth\labelwidth{#1} - \addtolength{\leftmargin}{\labelwidth} - \addtolength{\leftmargin}{2pt} - \def\makelabel##1{\hss \llap{##1}}% - \fi - \fi} - \fi - } -\let\endenumerate\endlist - -% Changes to the list parameters for itemize -\def\itemargs{% - \partopsep \z@ - \if@contemporary - \if@large - \ifnum\@itemdepth >1\fontsize{7.75bp}{11bp}\selectfont\itemsep1\p@\else\fontsize{8bp}{11.5bp}\selectfont\itemsep0\p@\fi - \else - \if@medium\ifnum\@itemdepth >1\itemsep0.5\p@\else\itemsep0\p@\fi\else\itemsep1\p@\fi - \fi - \else\itemsep\z@\fi - \parsep \z@ - \labelsep \if@contemporary1em\else\if@traditional5.5pt\else\if@modern.7em\else.7em\fi\fi\fi - \rightmargin \z@ - \listparindent \parindent - \itemindent \z@ - \if@contemporary\ifnum\@itemdepth >1\topsep 2\p@\else\topsep 6.5\p@\fi\else - \if@traditional - \ifnum\@itemdepth >1\topsep 0\p@\else\topsep 2mm\fi - \else\if@modern\ifnum \@itemdepth >1\topsep0\p@\else\topsep7\p@\fi - \setlength{\leftmarginii}{2mm} - \setlength{\leftmargini}{4mm} - \else\topsep 7\p@\fi\fi\fi -} - -\def\itemize{% - \@ifnextchar[{\@itemize}{\@itemize[$\bullet$]}} - -\def\@itemize[#1]{% - \ifnum \@itemdepth >3 \@toodeep\else - \advance\@itemdepth \@ne - \edef\@itemctr{item\romannumeral\the\@itemdepth} - \list{\csname label\@itemctr\endcsname}{% - \itemargs - \setlength{\leftmargin}{\csname leftmargin\romannumeral\the\@itemdepth\endcsname} - \if@traditional - \ifnum\@itemdepth >1 - \settowidth\labelwidth{#1} - \addtolength{\leftmargin}{\labelwidth} - \addtolength{\leftmargin}{\labelsep} - \else - \setlength\labelwidth{3mm} - \addtolength{\leftmargin}{\labelwidth} - \addtolength{\leftmargin}{\labelsep} - \fi - \else - \settowidth\labelwidth{#1} - \addtolength{\leftmargin}{\labelwidth} - \if@modern\else\addtolength{\leftmargin}{\labelsep}\fi - \fi - \def\makelabel##1{\hss \llap{##1}}}% - \fi - } -\let\enditemize\endlist - -\newenvironment{unlist}{% - \begin{list}{}% - {\setlength{\labelwidth}{\z@}% - \setlength{\labelsep}{\z@}% - \partopsep\z@ - \if@contemporary\setlength{\topsep}{6.5\p@}\else\if@traditional\setlength{\topsep}{5mm}\else\setlength{\topsep}{\medskipamount}\fi\fi% - \if@contemporary\setlength{\itemsep}{2\p@}\else\if@traditional\setlength{\itemsep}{0\p@}\else\setlength{\itemsep}{3\p@}\fi\fi% - \if@traditional\setlength{\leftmargin}{5mm}\setlength{\itemindent}{-3mm}\else - \setlength{\leftmargin}{2em}\setlength{\itemindent}{-2em}\fi - \parindent\z@}} -{\end{list}} - - -% *********************** -% Quotes and Quotations * -% *********************** -\def\quotation{\par\begin{list}{}{ - \partopsep\z@ - \if@contemporary% - \setlength{\topsep}{4pt} - \setlength{\leftmargin}{9pt}% - \setlength{\rightmargin}{9pt}% - \else% - \if@traditional - \setlength{\topsep}{11.75bp} - \setlength{\leftmargin}{1em} - \setlength{\rightmargin}{1em}% - \else - \setlength{\topsep}{\medskipamount} - \setlength{\leftmargin}{2em} - \setlength{\rightmargin}{\z@}% - \fi\fi% - \itemsep\z@% - \setlength\labelwidth{0pt}% - \setlength\labelsep{0pt}% - \listparindent\parindent}% - \if@contemporary\sffamily\fontseries{m}\selectfont\item[]{\hskip-9pt\color{grayfifty}\dhorline{\hsize}{1pt}\vskip3pt} - \else\if@traditional\fontsize{8.5bp}{11.75bp}\selectfont\else\fi\fi% - \item[]} -\def\endquotation{\if@contemporary\item[]{\hskip-9pt\color{grayfifty}\dhorline{\hsize}{1pt}}\fi% -\end{list}} - -\let\quote\quotation -\let\endquote\endquotation - -\def\episource#1{% -\if@contemporary% -\par\vskip5pt\hspace*{0pt}\hfill\sffamily\fontseries{m}\fontsize{7bp}{9bp}\selectfont\itshape#1\par% -\else\if@modern\par\hspace*{0pt}\hfill{\fontseries{b}\fontsize{8bp}{11.5}\selectfont#1}% -\else\par\hspace*{0pt}\hfill{\fontsize{8bp}{11.5}\selectfont#1}\fi\fi} -% -\newenvironment{epigraph}{\par%% -\if@contemporary\if@medium\addvspace{3pt}\else\addvspace{-6pt}\fi\sffamily\fontseries{m}% - \if@medium\fontsize{8bp}{11.5bp}\selectfont\else\fontsize{8bp}{11bp}\selectfont\fi% -\leftskip12.5pt\rightskip\leftskip% -\else\if@modern\addvspace{0pt}\fontsize{9bp}{11.5}\selectfont\leftskip9pt\rightskip\leftskip% -\else\addvspace{0pt}\fontsize{9bp}{11.5}\selectfont\leftskip9pt\rightskip\leftskip% -\fi\fi% -\let\source\episource}{\par\addvspace{3pt}} - - - -\skip\@mpfootins = \skip\footins -\fboxsep=6\p@ -\fboxrule=1\p@ - -% ******************* -% Table of contents * -% ******************* -\newcommand\@pnumwidth{4em} -\newcommand\@tocrmarg{2.55em plus 1fil} -\newcommand\@dotsep{1000} -\setcounter{tocdepth}{4} - -\def\numberline#1{\hbox to \@tempdima{{#1}}} - -\def\@authortocline#1#2#3#4#5{% - \vskip 1.5\p@ - \ifnum #1>\c@tocdepth \else - {\leftskip #2\relax \rightskip \@tocrmarg \parfillskip -\rightskip - \parindent #2\relax\@afterindenttrue - \interlinepenalty\@M - \leavevmode - \@tempdima #3\relax - \advance\leftskip \@tempdima \null\nobreak\hskip -\leftskip - {\itshape #4}\nobreak - \leaders\hbox{$\m@th - \mkern \@dotsep mu\hbox{.}\mkern \@dotsep - mu$}\hfill - \nobreak - \hb@xt@\@pnumwidth{\hfil}% - \par}% - \fi} - -\newcommand*\l@author{\@authortocline{2}{0pt}{30pt}} -\newcommand*\l@section{\@dottedtocline{3}{11pt}{20pt}} -\newcommand*\l@subsection{\@dottedtocline{4}{31pt}{29pt}} -\newcommand*\l@subsubsection[2]{} - - - -% *********** -% Footnotes * -% *********** - -\def\footnoterule{\if@traditional\hrule\@width \z@\@height 0pt\kern2pt\else% -\if@modern\noindent\rule{\columnwidth}{0.5pt}\else\hrule\@width \columnwidth\@height 0.5pt\kern2pt\fi\fi} - -\def\@makefnmark{\@textsuperscript{\normalfont\@thefnmark}}% - -\renewcommand\@makefntext[1]{\noindent\if@traditional\@hangfrom{\hbox to 4mm{\hfill\@makefnmark\hskip1.5mm}}\else{\@makefnmark\enskip}\fi#1} - -% *********** -% References * -% *********** - -\providecommand{\newblock}{} -\newenvironment{thebibliography}{% - \section{\bibname}% - \begingroup - \small - \begin{list}{}{% - \setlength{\topsep}{\z@}% - \setlength{\labelsep}{\z@}% - \settowidth{\labelwidth}{\z@}% - \setlength{\leftmargin}{4mm}% - \setlength{\itemindent}{-4mm}}\small} -{\end{list}\endgroup} - -\RequirePackage{natbib} - -%\renewcommand\NAT@force@numbers{% -% \ifNAT@numbers\else -% \PackageError{natbib}{Bibliography not compatible with author-year -% citations.\MessageBreak -% Press to continue in numerical citation style} -% {Check the bibliography entries for non-compliant syntax,\MessageBreak -% or select author-year BibTeX style, e.g. plainnat}% -% \global\NAT@numberstrue\fi} - -\def\NAT@force@numbers{\if@numbib\global\NAT@numberstrue\else\global\NAT@numbersfalse\fi}% - -\if@modern\if@medium\def\bibfont{\fontsize{7.5bp}{10}\selectfont}\else\fi -\else -\if@traditional\def\bibfont{\fontsize{9bp}{11.75bp}\selectfont} -\else -\if@contemporary\def\bibfont{\fontsize{7.75bp}{11bp}\selectfont}% -\else -\fi -\fi -\fi - -\if@contemporary - \renewcommand\@biblabel[1]{#1.\hspace*{10pt}\hfill} - \bibhang=12pt - \bibsep=\z@ -\else -\if@traditional - \renewcommand\@biblabel[1]{#1.\hspace*{5.5pt}\hfill} - \bibhang=12pt - \bibsep=\z@ -\else -\if@modern - \renewcommand\@biblabel[1]{#1.\hfill\hspace*{3pt}} -\else - \renewcommand\@biblabel[1]{#1.\hspace*{10pt}\hfill} - \bibhang=1em -\fi -\fi -\fi - -\renewcommand\NAT@bibsetnum[1]{\settowidth\labelwidth{\@biblabel{#1}}% - \setlength{\leftmargin}{\labelwidth}\addtolength{\leftmargin}{\labelsep}% - \setlength{\itemsep}{\bibsep}\setlength{\parsep}{\z@}% - \ifNAT@openbib - \addtolength{\leftmargin}{\bibindent}% - \setlength{\itemindent}{-\bibindent}% - \setlength{\listparindent}{\itemindent}% - \setlength{\parsep}{0pt}% - \fi -} -\if@traditional -\if@large -\def\bibsection{\section*{\fontsize{10bp}{12}\fontseries{b}\selectfont\leftskip0pt plus1fill\rightskip0pt plus1fill \uppercase{References}\vspace*{5pt}}} -\else -\def\bibsection{\section*{\fontsize{9bp}{12}\fontseries{b}\selectfont\leftskip0pt plus1fill\rightskip0pt plus1fill \uppercase{References}\vspace*{-2.5pt}}} -\fi -\fi - - -%Math parameters - -\setlength{\jot}{5\p@} -\mathchardef\@m=1500 % adapted value - -\def\frenchspacing{\sfcode`\.\@m \sfcode`\?\@m \sfcode`\!\@m - \sfcode`\:\@m \sfcode`\;\@m \sfcode`\,\@m} - -% Theorems -\def\th@plain{% -%% \let\thm@indent\noindent % no indent -\thm@headfont{\quad\scshape}% heading font is bold -\thm@notefont{\upshape\mdseries}% same as heading font -\thm@headpunct{.}% no period after heading -\thm@headsep 5\p@ plus\p@ minus\p@\relax -%% \let\thm@swap\@gobble -%% \thm@preskip\topsep -%% \thm@postskip\theorempreskipamount -\itshape % body font -} - -\vbadness=9999 -\tolerance=9999 -\doublehyphendemerits=10000 -\doublehyphendemerits 640000 -\finalhyphendemerits 1000000 - -%%\raggedbottom -\flushbottom -\frenchspacing -\ps@headings - -\if@mediumone -\else -\if@small% -\else% -\twocolumn% -\fi% -\fi% - -% Screen PDF compatability -\newcommand{\medline}[1]{% - \unskip\unskip\ignorespaces} - - -%%%%for smaller size text -\newenvironment{methods}{% - \begingroup -\def\section{% - \@startsection{section}{1}{\z@} - {-24\p@ plus -3\p@}{4\p@} - {\reset@font\raggedright\sffamilyfontbold\fontsize{10bp}{12}\selectfont}} - \def\subsection{% - \@startsection{subsection}{2}{\z@} - {-11\p@ plus -2\p@}{4\p@} - {\reset@font\raggedright\sffamilyfont\fontsize{9bp}{12}\selectfont}} -\def\subsubsection{% - \@startsection{subsubsection}{3}{\z@} - {-11\p@ plus -1\p@}{0.001em} - {\reset@font\normalfont\mathversion{bold}\normalsize\bfseries}} -\normalsize - \par} -{\par\endgroup\bigskip\@afterheading\@afterindentfalse} - - -%\usepackage{lstlang1} - -\language=2 - -\hyphenation{Figure Table Figures Tables} - -%%%%%%%%%%%%%%% Biography -% -\RequirePackage{wrapfig}% -% -\newcount\wraplines% -\wraplines=8% -% -\newbox\@authorfigbox% -\newskip\@authorfigboxdim% -% -\newskip\biofigadjskip% -\biofigadjskip=0pt% -% -\def\authbiotextfont{\reset@font\fontsize{8bp}{9.5bp}\selectfont}% -% -\newenvironment{biography}[2]{\par\addvspace{11.5pt plus3.375pt minus1.6875pt}%\lineno@off% -\def\author##1{{\if@modern\sf\else\fi\bfseries##1}}% -\if!#1!\def\@authorfig{}\else\def\@authorfig{{#1}}\fi% -\setbox\@authorfigbox=\hbox{#1}% -\@authorfigboxdim=\wd\@authorfigbox% -\advance\@authorfigboxdim by -10pt -\wraplines=7\fboxrule=1pt\fboxsep=6pt% -\noindent{% -\ifx\@authorfig\@empty\else\unskip% -\begin{wrapfigure}[\wraplines]{l}[0pt]{\@authorfigboxdim}%{38.25mm}% -\if@traditional\if@medium\vskip-19pt\else\vskip-19pt\fi\else\vskip-19pt\fi\vskip\biofigadjskip% -\@authorfig% -\end{wrapfigure}% -\fi% -{\authbiotextfont#2\par}% -\par% -}}{\par\addvspace{10.5pt plus3.375pt minus1.6875pt}} -% - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Theorem %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -\RequirePackage{amsthm}% -% -\@ifpackageloaded{amsthm}{% -% -\let\proof\relax% -\let\endproof\relax% - -\def\@begintheorem#1#2[#3]{% - \deferred@thm@head{\the\thm@headfont \thm@indent - \@ifempty{#1}{\let\thmname\@gobble}{\let\thmname\@iden}% - \@ifempty{#2}{\let\thmnumber\@gobble}{\let\thmnumber\@iden}% - \@ifempty{#3}{\let\thmnote\@gobble}{\let\thmnote\@iden}% - \thm@swap\swappedhead\thmhead{#1}{#2}{#3}% - \the\thm@headpunct - \thmheadnl % possibly a newline. - \hskip\thm@headsep - }% - \ignorespaces -} - -\def\@endtheorem{\endtrivlist\@endpefalse} - - -\AtBeginDocument{% -% -\DeclareSymbolFont{AMSa}{U}{msa}{m}{n}% -\DeclareMathSymbol{\opensquare}{\mathord}{AMSa}{"03}% -\def\qedsymbol{\ensuremath{\opensquare}}% -% -\newenvironment{proof}[1][\proofname]{\par\removelastskip%\vspace*{2pt}% - \pushQED{\ensuremath{\qed}}% - \normalfont \topsep7.5\p@\@plus7.5\p@\relax% - \trivlist% - \item[\hskip\labelsep% - \itshape% - #1\ \@addpunct{}]\ignorespaces% -}{% - \popQED\endtrivlist\@endpefalse% -}}% -% -\def\thm@space@setup{% -\thm@preskip=12pt% -\thm@postskip=12pt} -% - -%%%%%%%%%%%%%%%%%% StyleOne -% -\newtheoremstyle{thmstyleone}% Numbered -{\if@contemporary\if@small11pt plus2pt minus1pt\else11.5pt plus2pt minus1pt\fi\else -\if@traditional11.75pt plus2pt minus1pt\else12pt plus2pt minus1pt\fi\fi}% Space above -{\if@contemporary\if@small11pt plus2pt minus1pt\else11.5pt plus2pt minus1pt\fi\else -\if@traditional11.75pt plus2pt minus1pt\else12pt plus2pt minus1pt\fi\fi}% Space below -{\if@contemporary\sffamily\fontseries{m}\selectfont\leftskip12pt\else -\if@traditional\normalsize\leftskip6mm\rightskip3mm plus1fill\else\normalfont\itshape\fi\fi}% Body font -{\if@traditional-3mm\else0pt\fi}% Indent amount -{\if@contemporary\sffamily\selectfont\bfseries\else\bfseries\fi}% Theorem head font -{}% Punctuation after theorem head -{.5em}% Space after theorem headi -{\thmname{#1}\thmnumber{\@ifnotempty{#1}{ }\@upn{#2}}% - \thmnote{ {\the\thm@notefont(#3)}}}% Theorem head spec (can be left empty, meaning `normal') -% -\newtheoremstyle{thmstyletwo}% Numbered -{\if@contemporary\if@small11pt plus2pt minus1pt\else11.5pt plus2pt minus1pt\fi\else -\if@traditional11.75pt plus2pt minus1pt\else12pt plus2pt minus1pt\fi\fi}% Space above -{\if@contemporary\if@small11pt plus2pt minus1pt\else11.5pt plus2pt minus1pt\fi\else -\if@traditional11.75pt plus2pt minus1pt\else12pt plus2pt minus1pt\fi\fi}% Space below -{\if@contemporary\sffamily\fontseries{m}\selectfont\leftskip12pt\else -\if@traditional\normalsize\leftskip6mm\rightskip3mm plus1fill\else\normalfont\itshape\fi\fi}% Body font -{\if@traditional-3mm\else0pt\fi}% Indent amount -{\if@contemporary\sffamily\selectfont\bfseries\else\if@traditional\bfseries\else\if@modern\normalfont\fi\fi\fi}% Theorem head font -{}% Punctuation after theorem head -{.5em}% Space after theorem headi -{\thmname{#1}\thmnumber{\@ifnotempty{#1}{ }{#2}}% - \thmnote{ {\the\thm@notefont(#3)}}}% Theorem head spec (can be left empty, meaning `normal') -% -\newtheoremstyle{thmstylethree}% Definition -{\if@contemporary\if@small11pt plus2pt minus1pt\else11.5pt plus2pt minus1pt\fi\else -\if@traditional11.75pt plus2pt minus1pt\else12pt plus2pt minus1pt\fi\fi}% Space above -{\if@contemporary\if@small11pt plus2pt minus1pt\else11.5pt plus2pt minus1pt\fi\else -\if@traditional11.75pt plus2pt minus1pt\else12pt plus2pt minus1pt\fi\fi}% Space below -{\if@contemporary\sffamily\fontseries{m}\selectfont\leftskip12pt\else% -\if@traditional\normalsize\leftskip6mm\rightskip3mm plus1fill\else\if@modern\normalfont\fi\fi\fi}% Body font -{\if@traditional-3mm\else0pt\fi}% Indent amount -{\if@contemporary\sffamily\selectfont\bfseries\else\bfseries\fi}% Theorem head font -{}% Punctuation after theorem head -{.5em}% Space after theorem headi -{\thmname{#1}\thmnumber{\@ifnotempty{#1}{ }\@upn{#2}}% - \thmnote{ {\the\thm@notefont(#3)}}}% Theorem head spec (can be left empty, meaning `normal') -% -\newtheoremstyle{thmstylefour}% Proof -{\if@contemporary\if@small11pt plus2pt minus1pt\else11.5pt plus2pt minus1pt\fi\else -\if@traditional11.75pt plus2pt minus1pt\else12pt plus2pt minus1pt\fi\fi}% Space above -{\if@contemporary\if@small11pt plus2pt minus1pt\else11.5pt plus2pt minus1pt\fi\else -\if@traditional11.75pt plus2pt minus1pt\else12pt plus2pt minus1pt\fi\fi}% Space below -{\if@contemporary\sffamily\fontseries{m}\selectfont\leftskip12pt\else -\if@traditional\normalsize\leftskip6mm\rightskip3mm plus1fill\else\if@modern\normalfont\else\normalfont\itshape\fi\fi\fi}% Body font -{\if@traditional-3mm\else0pt\fi}% Indent amount -{\if@contemporary\sffamily\selectfont\bfseries\else\if@modern\itshape\else\bfseries\fi\fi}% Theorem head font -{}% Punctuation after theorem head -{.5em}% Space after theorem headi -{\global\proofthmtrue\thmname{#1} \thmnote{#3}}% Theorem head spec (can be left empty, meaning `normal') -% -}{} - -\def\sbond{\ensuremath{\raise.25ex\hbox{${-}\!\!\!\!{-}$}}\kern -.9pt} -\def\dbond{\ensuremath{\raise.25ex\hbox{=$\!$=}}} -\def\tbond{\ensuremath{\raise.20ex\hbox{${\equiv}\!\!\!{\equiv}$}}} - -\newcommand{\query}[2][0pt]{}% - -\renewcommand{\dag}{{\mathversion{normal}$^{\dagger}$}} - -%% Additional Packages -\RequirePackage[countmax]{subfloat}%,subfig -\AtBeginDocument{\usepackage{anyfontsize}}% -\RequirePackage{multirow} -\RequirePackage{footnote} -\RequirePackage{url} -\RequirePackage{amsmath} -\RequirePackage{mathrsfs} -\RequirePackage{algorithm}% -\RequirePackage{algorithmicx}% -\RequirePackage{algpseudocode}% -\RequirePackage{listings}% - -%% Appendix Macro Begins %%% - -\newif\if@chapter@pp\@chapter@ppfalse -\newif\if@knownclass@pp\@knownclass@ppfalse -\@ifundefined{chapter}{% - \@ifundefined{section}{}{\@knownclass@pptrue}}{% - \@chapter@pptrue\@knownclass@pptrue} -\providecommand{\phantomsection}{} -\newcounter{@pps} - \renewcommand{\the@pps}{\alph{@pps}} -\newif\if@pphyper - \@pphyperfalse -\AtBeginDocument{% - \@ifpackageloaded{hyperref}{\@pphypertrue}{}} - -\newif\if@dotoc@pp\@dotoc@ppfalse -\newif\if@dotitle@pp\@dotitle@ppfalse -\newif\if@dotitletoc@pp\@dotitletoc@ppfalse -\newif\if@dohead@pp\@dohead@ppfalse -\newif\if@dopage@pp\@dopage@ppfalse -\DeclareOption{toc}{\@dotoc@pptrue} -\DeclareOption{title}{\@dotitle@pptrue} -\DeclareOption{titletoc}{\@dotitletoc@pptrue} -\DeclareOption{header}{\@dohead@pptrue} -\DeclareOption{page}{\@dopage@pptrue} -\ProcessOptions\relax -\newcommand{\@ppendinput}{} -\if@knownclass@pp\else - \PackageWarningNoLine{appendix}% - {There is no \protect\chapter\space or \protect\section\space command.\MessageBreak - The appendix package will not be used} - \renewcommand{\@ppendinput}{\endinput} -\fi -\@ppendinput - -\newcommand{\appendixtocon}{\@dotoc@pptrue} -\newcommand{\appendixtocoff}{\@dotoc@ppfalse} -\newcommand{\appendixpageon}{\@dopage@pptrue} -\newcommand{\appendixpageoff}{\@dopage@ppfalse} -\newcommand{\appendixtitleon}{\@dotitle@pptrue} -\newcommand{\appendixtitleoff}{\@dotitle@ppfalse} -\newcommand{\appendixtitletocon}{\@dotitletoc@pptrue} -\newcommand{\appendixtitletocoff}{\@dotitletoc@ppfalse} -\newcommand{\appendixheaderon}{\@dohead@pptrue} -\newcommand{\appendixheaderoff}{\@dohead@ppfalse} -\newcounter{@ppsavesec} -\newcounter{@ppsaveapp} -\setcounter{@ppsaveapp}{0} -\newcommand{\@ppsavesec}{% - \if@chapter@pp \setcounter{@ppsavesec}{\value{chapter}} \else - \setcounter{@ppsavesec}{\value{section}} \fi} -\newcommand{\@pprestoresec}{% - \if@chapter@pp \setcounter{chapter}{\value{@ppsavesec}} \else - \setcounter{section}{\value{@ppsavesec}} \fi} -\newcommand{\@ppsaveapp}{% - \if@chapter@pp \setcounter{@ppsaveapp}{\value{chapter}} \else - \setcounter{@ppsaveapp}{\value{section}} \fi} -\newcommand{\restoreapp}{% - \if@chapter@pp \setcounter{chapter}{\value{@ppsaveapp}} \else - \setcounter{section}{\value{@ppsaveapp}} \fi} -\providecommand{\appendixname}{Appendix} -\newcommand{\appendixtocname}{Appendices} -\newcommand{\appendixpagename}{Appendices} -\newcommand{\appendixpage}{% - \if@chapter@pp \@chap@pppage \else \@sec@pppage \fi -} -\newcommand{\clear@ppage}{% - \if@openright\cleardoublepage\else\clearpage\fi} - -\newcommand{\@chap@pppage}{% - \clear@ppage - \thispagestyle{plain}% - \if@twocolumn\onecolumn\@tempswatrue\else\@tempswafalse\fi - \null\vfil - \markboth{}{}% - {\centering - \interlinepenalty \@M - \normalfont - \Huge \bfseries \appendixpagename\par}% - \if@dotoc@pp - \addappheadtotoc - \fi - \vfil\newpage - \if@twoside - \if@openright - \null - \thispagestyle{empty}% - \newpage - \fi - \fi - \if@tempswa - \twocolumn - \fi -} - -\newcommand{\@sec@pppage}{% - \par - \addvspace{4ex}% - \@afterindentfalse - {\parindent \z@ \raggedright - \interlinepenalty \@M - \normalfont - \huge \bfseries \appendixpagename% - \markboth{}{}\par}% - \if@dotoc@pp - \addappheadtotoc - \fi - \nobreak - \vskip 3ex - \@afterheading -} - -\newif\if@pptocpage - \@pptocpagetrue -\newcommand{\noappendicestocpagenum}{\@pptocpagefalse} -\newcommand{\appendicestocpagenum}{\@pptocpagetrue} -\newcommand{\addappheadtotoc}{% - \phantomsection - \if@chapter@pp - \if@pptocpage - \addcontentsline{toc}{chapter}{\appendixtocname}% - \else - \if@pphyper - \addtocontents{toc}% - {\protect\contentsline{chapter}{\appendixtocname}{}{\@currentHref}}% - \else - \addtocontents{toc}% - {\protect\contentsline{chapter}{\appendixtocname}{}}% - \fi - \fi - \else - \if@pptocpage - \addcontentsline{toc}{section}{\appendixtocname}% - \else - \if@pphyper - \addtocontents{toc}% - {\protect\contentsline{section}{\appendixtocname}{}{\@currentHref}}% - \else - \addtocontents{toc}% - {\protect\contentsline{section}{\appendixtocname}{}}% - \fi - \fi - \fi -} - -\providecommand{\theH@pps}{\alph{@pps}} - -\newcommand{\@resets@pp}{\par - \@ppsavesec - \stepcounter{@pps} - \setcounter{section}{0}% - \if@chapter@pp - \setcounter{chapter}{0}% - \renewcommand\@chapapp{\appendixname}% - \renewcommand\thechapter{\@Alph\c@chapter}% - \else - \setcounter{subsection}{0}% - \renewcommand\thesection{\@Alph\c@section}% - \fi - \if@pphyper - \if@chapter@pp - \renewcommand{\theHchapter}{\theH@pps.\Alph{chapter}}% - \else - \renewcommand{\theHsection}{\theH@pps.\Alph{section}}% - \fi - \xdef\Hy@chapapp{\Hy@appendixstring}% - \fi - \restoreapp -} - -\newenvironment{appendices}{% - \@resets@pp - \if@dotoc@pp - \if@dopage@pp % both page and toc - \if@chapter@pp % chapters - \clear@ppage - \fi - \appendixpage - \else % toc only - \if@chapter@pp % chapters - \clear@ppage - \fi - \addappheadtotoc - \fi - \else - \if@dopage@pp % page only - \appendixpage - \fi - \fi - \if@chapter@pp - \if@dotitletoc@pp \@redotocentry@pp{chapter} \fi - \else - \if@dotitletoc@pp \@redotocentry@pp{section} \fi - \if@dohead@pp - \def\sectionmark##1{% - \if@twoside - \markboth{\@formatsecmark@pp{##1}}{} - \else - \markright{\@formatsecmark@pp{##1}}{} - \fi} - \fi - \if@dotitle@pp - \def\sectionname{\appendixname} - \def\@seccntformat##1{\@ifundefined{##1name}{}{\csname ##1name\endcsname\ }% - \csname the##1\endcsname\quad} - \fi - \fi}{% - \@ppsaveapp\@pprestoresec} - -\newcommand{\setthesection}{\thechapter.\Alph{section}} -\newcommand{\setthesubsection}{\thesection.\Alph{subsection}} - -\newcommand{\@resets@ppsub}{\par - \stepcounter{@pps} - \if@chapter@pp - \setcounter{section}{0} - \renewcommand{\thesection}{\setthesection} - \else - \setcounter{subsection}{0} - \renewcommand{\thesubsection}{\setthesubsection} - \fi - \if@pphyper - \if@chapter@pp - \renewcommand{\theHsection}{\theH@pps.\setthesection}% - \else - \renewcommand{\theHsubsection}{\theH@pps.\setthesubsection}% - \fi - \xdef\Hy@chapapp{\Hy@appendixstring}% - \fi -} - -\newenvironment{subappendices}{% - \@resets@ppsub - \if@chapter@pp - \if@dotitletoc@pp \@redotocentry@pp{section} \fi - \if@dotitle@pp - \def\sectionname{\appendixname} - \def\@seccntformat##1{\@ifundefined{##1name}{}{\csname ##1name\endcsname\ }% - \csname the##1\endcsname\quad} - \fi - \else - \if@dotitletoc@pp \@redotocentry@pp{subsection} \fi - \if@dotitle@pp - \def\subsectionname{\appendixname} - \def\@seccntformat##1{\@ifundefined{##1name}{}{\csname ##1name\endcsname\ }% - \csname the##1\endcsname\quad} - \fi - \fi}{} - -\newcommand{\@formatsecmark@pp}[1]{% - \MakeUppercase{\appendixname\space - \ifnum \c@secnumdepth >\z@ - \thesection\quad - \fi - #1}} -\newcommand{\@redotocentry@pp}[1]{% - \let\oldacl@pp=\addcontentsline - \def\addcontentsline##1##2##3{% - \def\@pptempa{##1}\def\@pptempb{toc}% - \ifx\@pptempa\@pptempb - \def\@pptempa{##2}\def\@pptempb{#1}% - \ifx\@pptempa\@pptempb -\oldacl@pp{##1}{##2}{\appendixname\space ##3}% - \else - \oldacl@pp{##1}{##2}{##3}% - \fi - \else - \oldacl@pp{##1}{##2}{##3}% - \fi} -} - -%%% Appendix Macro Ends %% - - -\RequirePackage[hidelinks]{hyperref} - -\RequirePackage{tikz} - -\usetikzlibrary{svg.path} - -\usetikzlibrary{decorations.markings} -\newcommand{\dhorline}[3][0]{% - \tikz[baseline]{\path[decoration={markings, - mark=between positions 0 and 1 step 2*#3 - with {\node[fill, circle, minimum width=#3, inner sep=0pt, anchor=south west] {};}},postaction={decorate}] (0,#1) -- ++(#2,0);}} - - -\definecolor{orcidlogocol}{HTML}{A6CE39} -\tikzset{ - orcidlogo/.pic={ - \fill[orcidlogocol] svg{M256,128c0,70.7-57.3,128-128,128C57.3,256,0,198.7,0,128C0,57.3,57.3,0,128,0C198.7,0,256,57.3,256,128z}; - \fill[white] svg{M86.3,186.2H70.9V79.1h15.4v48.4V186.2z} - svg{M108.9,79.1h41.6c39.6,0,57,28.3,57,53.6c0,27.5-21.5,53.6-56.8,53.6h-41.8V79.1z M124.3,172.4h24.5c34.9,0,42.9-26.5,42.9-39.7c0-21.5-13.7-39.7-43.7-39.7h-23.7V172.4z} - svg{M88.7,56.8c0,5.5-4.5,10.1-10.1,10.1c-5.6,0-10.1-4.6-10.1-10.1c0-5.6,4.5-10.1,10.1-10.1C84.2,46.7,88.7,51.3,88.7,56.8z}; - } -} - -%% Reciprocal of the height of the svg whose source is above. The -%% original generates a 256pt high graphic; this macro holds 1/256. -\newcommand{\@OrigHeightRecip}{0.00390625} - -%% We will compute the current X height to make the logo the right height -\newlength{\@curXheight} - -\DeclareRobustCommand\ORCID[1]{% -\texorpdfstring{% -\setlength{\@curXheight}{\fontcharht\font`X}% -\href{https://orcid.org/#1}{\XeTeXLinkBox{\mbox{% -\begin{tikzpicture}[yscale=-\@OrigHeightRecip*\@curXheight, -xscale=\@OrigHeightRecip*\@curXheight,transform shape] -\pic{orcidlogo}; -\end{tikzpicture}% -}}}}{}} - -\def \@fpsadddefault {% - \edef \@fps {\@fps\csname fps@\@captype \endcsname}% -} - - -%\RequirePackage[most]{tcolorbox} - -\newif\if@boxenvpresent\global\@boxenvpresentfalse -% -%%% -\def\boxtypehdstyle{\reset@font -\if@contemporary\sffamily\fontsize{9bp}{11bp}\selectfont\raggedright\else -\if@traditional\fontsize{10bp}{11.75bp}\selectfont\else\fontsize{10bp}{11.5bp}\selectfont -\fi\fi}% -% -\def\boxtypetextAstyle{\reset@font -\if@contemporary\sffamily\fontseries{m}\fontsize{7.5bp}{11.5bp}\selectfont\else -\if@traditional\fontsize{9bp}{11.75bp}\selectfont\else\fontsize{7.5bp}{11.5bp}\selectfont -\fi\fi% -\parindent=0bp} -% -\def\boxtypeonehdstyle{\reset@font -\if@contemporary\sffamily\if@large\fontsize{9bp}{11.5bp}\selectfont\else\fontsize{9bp}{11bp}\selectfont\fi\bfseries\else -\if@traditional\fontsize{9bp}{11.75bp}\selectfont\bfseries\else\fontsize{9bp}{11bp}\selectfont\bfseries\fi\fi} - -\def\boxtypetwohdstyle{\reset@font -\if@contemporary\sffamily\if@large\fontsize{9bp}{11.5bp}\selectfont\else\fontsize{9bp}{11bp}\selectfont\fi\itshape\else -\if@traditional\fontsize{9bp}{11.75bp}\selectfont\else\fontsize{9bp}{11bp}\selectfont\fi\fi} - -\def\boxtypethreehdstyle{\reset@font -\if@contemporary\sffamily\if@large\fontsize{7.5bp}{10.5bp}\selectfont\else\fontsize{7.5bp}{10bp}\selectfont\fi\bfseries\else -\if@traditional\fontsize{9bp}{11.75bp}\selectfont\itshape\else\fontsize{7.5bp}{10bp}\selectfont\fi\fi} - -\def\boxtypefootnotetxt{\reset@font\if@traditional\fontsize{6.5bp}{9bp}\selectfont\else\sffamily\fontsize{6.5bp}{9bp}\selectfont\fi} - -\def\boxnameA{Box} -\gdef\theBoxone{\@arabic\c@tcb@cnt@BoxTypeA} - -\def\boxfootnotetxt#1{{\par\vskip0pt\noindent{\boxtypefootnotetxt\selectfont\leftskip-6pt\rightskip-6pt#1\par}}} - -\newenvironment{Figure}[1][]{\par\addvspace{9pt}\let\@makecaption\@figurecaption\def\@captype{figure}}{\par\addvspace{9pt}} - -\newenvironment{Table}[1][]{\par\addvspace{9pt}\def\@captype{table}}{\par\addvspace{9pt}} - -\newcounter{boxnumcnt} -\setcounter{boxnumcnt}{0} - - -\newenvironment{boxtext}[1][] -{\par\addvspace{12pt}\global\@boxenvpresenttrue\urlstyle{rm}\refstepcounter{boxnumcnt}% -\def\@xfloat##1[{\@ifnextchar{H}{\@float@HH{##1}[}{\@float@Hx{##1}[}}%% reinstate float.sty macro (over floatpag) -\gdef\@envopt{#1}% -\let\footnotesize\boxtypefootnotetxt% -\setlength{\footnotesep}{11\p@}\columnsep=12pt% -\setlength{\skip\@mpfootins}{9\p@ \@plus 1 \p@} % \@minus 4\p@ -\renewcommand\@makefntext[1]{\def\thempfootnote{{\fontsize{10bp}{12bp}\selectfont\itshape\@alph\c@mpfootnote}}% -\def\thempfn{\thempfootnote} -%%\def\@makefnmark{\hbox{\@textsuperscript{\normalfont\@thefnmark}}}%% -\if@contemporary% - \setlength\parindent{1em}% -\else% -\if@traditional - \setlength\parindent{8pt}% -\else -\if@modern - \setlength\parindent{12pt}% -\else - \setlength\parindent{12pt}% -\fi% -\fi% -\fi - \noindent%\raggedright - {\hbox{\smash{\rmfamily\@makefnmark\hskip3pt}}}##1\vphantom{p}\par\vspace*{-3.5pt}}% -\setlength{\abovecaptionskip}{6pt}% -\def\boxsection{\@startsection {section}{1}{\z@}% - {\if@contemporary\if@large-9pt \@plus -2pt\else-10.5pt \@plus -2pt\fi\else - \if@traditional-3.44mm \@plus -2pt\else-9pt \@plus -2pt\fi\fi}% - {\if@contemporary2.5pt\else\if@traditional0.001pt\else0.001pt\fi\fi}% - {\boxtypeonehdstyle}} -\def\boxsubsection{\@startsection{subsection}{2}{\z@}% - {\if@contemporary\if@large-9pt \@plus -2pt\else-10.5pt \@plus -2pt\fi\else - \if@traditional-3.44mm \@plus -2pt\else-9pt \@plus -2pt\fi\fi}% - {\if@contemporary2.5pt\else\if@traditional0.001pt\else0.001pt\fi\fi}% - {\boxtypetwohdstyle}} -\def\boxsubsubsection{\@startsection{subsubsection}{3}{\z@}% - {\if@contemporary\if@large-6pt \@plus -2pt\else-7pt \@plus -2pt\fi\else - \if@traditional-4.155mm \@plus -2pt\else-7pt \@plus -2pt\fi\fi}% - {-6pt}% - {\def\@sechdenddot{}\boxtypethreehdstyle}} -\let\section\boxsection -\let\subsection\boxsubsection -\let\subsubsection\boxsubsubsection -\let\figure\Figure\let\endfigure\endFigure% -%\ifx\@envopt\empty\begin{BoxTypeA}[#1]\else\begin{BoxTypeA}[#1]\fi -\noindent\hbox to \hsize{\rule{6pc}{0.5pt}\hfill}\par\nointerlineskip\vskip8pt\nointerlineskip\par\leftskip8pt\rightskip8pt% -{\boxtypehdstyle\leftskip8pt\textbf{\boxnameA~\theboxnumcnt}\enspace#1\par}\par\nointerlineskip\vskip8pt\nointerlineskip\par\noindent\@afterindentfalse\noindent\ignorespaces\boxtypetextAstyle}% -{\par\nointerlineskip\vskip8pt\nointerlineskip\par\noindent\hbox to \hsize{\hfill\rule{6pc}{0.5pt}\hskip8pt}\par\nointerlineskip\vskip8pt\nointerlineskip\par\vphantom{p}\par\vspace*{-2.5pt}%\ifx\@envopt\empty\end{BoxTypeA}\else\end{BoxTypeA}\fi -\addvspace{10pt}% -\@endparenv\global\@boxenvpresentfalse}% -% -% \if@contemporary -% \newtcolorbox[auto counter]{BoxTypeA}[1][]{% -% %label={#1}, -% enhanced,breakable, -% arc=0pt,outer arc=0pt,boxrule=1pt,boxsep=0pt, -% top={\if@large10.5pt\else11pt\fi},left={\if@large9pt\else8.5pt\fi},right={\if@large9pt\else8.5pt\fi}, -% bottom={\if@large9pt\else8.5pt\fi},middle=0pt, -% toptitle=7pt,bottomtitle=9pt,% -% colback=white, -% colbacktitle=graysevtyfive, -% colframe=graysevtyfive, coltitle=white, -% titlerule=0pt,titlerule style={white,line width=0pt}, -% title={{\textbf{\boxnameA~\thetcbcounter:}\enspace}#1}, -% fonttitle=\boxtypehdstyle, fontupper=\boxtypetextAstyle, -% topsep at break=0pt,bottomsep at break=0pt, -% pad before break=0pt,pad after break=6pt,bottomrule at break=0pt,toprule at break=0pt}% -% \else -% \if@traditional -% \newtcolorbox[auto counter]{BoxTypeA}[1][]{% -% %label={#1}, -% enhanced,breakable, -% arc=0pt,outer arc=0pt,boxrule=0.5pt,boxsep=0pt, -% top=11pt,left=8.5pt,right=8.5pt,bottom=8.5pt,middle=0pt, -% toptitle=7pt,bottomtitle=6pt,% -% colback=white, -% colbacktitle=white, -% colframe=black, coltitle=black, -% titlerule=0pt,titlerule style={white,line width=0pt}, -% title={{\textbf{\boxnameA~\thetcbcounter}\enspace}#1}, -% fonttitle=\boxtypehdstyle, fontupper=\boxtypetextAstyle, -% topsep at break=0pt,bottomsep at break=0pt, -% pad before break=0pt,pad after break=6pt,bottomrule at break=0pt,toprule at break=0pt}% -% \else -% \if@modern -% \newtcolorbox[auto counter]{BoxTypeA}[1][]{% -% %label={#1}, -% enhanced,breakable, -% arc=0pt,outer arc=0pt,boxrule=0.5pt,boxsep=0pt, -% top=11pt,left=8.5pt,right=8.5pt,bottom=8.5pt,middle=0pt, -% toptitle=7pt,bottomtitle=6pt,% -% colback=white, -% colbacktitle=white, -% colframe=black, coltitle=black, -% titlerule=0pt,titlerule style={white,line width=0pt}, -% title={{\textbf{\boxnameA~\thetcbcounter}\enspace}#1}, -% fonttitle=\boxtypehdstyle, fontupper=\boxtypetextAstyle, -% topsep at break=0pt,bottomsep at break=0pt, -% pad before break=0pt,pad after break=6pt,bottomrule at break=0pt,toprule at break=0pt}% -% \else -% \fi% -% \fi% -% \fi% - -\def\boxbreak{\vspace*{-11pt}\pagebreak\vspace*{-11pt}} - -\def\@outputdblcol{% - \if@firstcolumn - \global\@firstcolumnfalse - \global\setbox\@leftcolumn\copy\@outputbox\ifnum\currentpageno=\c@page\enlargethispage{-12pt}\fi% - \splitmaxdepth\maxdimen - \vbadness\maxdimen - \setbox\@outputbox\vbox{\unvbox\@outputbox\unskip}% - \setbox\@outputbox\vsplit\@outputbox to\maxdimen - \toks@\expandafter{\topmark}% - \xdef\@firstcoltopmark{\the\toks@}% - \toks@\expandafter{\splitfirstmark}% - \xdef\@firstcolfirstmark{\the\toks@}% - \ifx\@firstcolfirstmark\@empty - \global\let\@setmarks\relax - \else - \gdef\@setmarks{% - \let\firstmark\@firstcolfirstmark - \let\topmark\@firstcoltopmark}% - \fi - \else - \global\@firstcolumntrue - \setbox\@outputbox\vbox{% - \hb@xt@\textwidth{% - \hb@xt@\columnwidth{\box\@leftcolumn\hss}% - \hfil - {\normalcolor\vrule \@width\columnseprule}% - \hfil - \hb@xt@\columnwidth{\box\@outputbox\hss}}}% - \@combinedblfloats - \@setmarks - \@outputpage - \begingroup - \@dblfloatplacement - \@startdblcolumn - \@whilesw\if@fcolmade \fi{\@outputpage - \@startdblcolumn}% - \endgroup - \fi}% - -\RequirePackage{silence} -\WarningFilter{latexfont}{Font shape} -\WarningFilter{latexfont}{Some font shapes} - -\immediate\write18{texcount -total -inc \jobname.tex > wordcount.txt} -\newcommand{\wordcount}{\input{wordcount.txt}} - -%% End here %% - -\endinput diff --git a/manuscript/shared/reference.bib b/manuscript/reference.bib similarity index 85% rename from manuscript/shared/reference.bib rename to manuscript/reference.bib index ae11e49..6ad8df2 100644 --- a/manuscript/shared/reference.bib +++ b/manuscript/reference.bib @@ -1,7 +1,7 @@ @article{ahmadUniProtWebsiteAPI2025, title = {The {{UniProt}} Website {{API}}: Facilitating Programmatic Access to Protein Knowledge}, shorttitle = {The {{UniProt}} Website {{API}}}, - author = {Ahmad, Shadab and {Jose~da~Costa~Gonzales}, Leonardo and {Bowler-Barnett}, Emily~H and Rice, Daniel~L and Kim, Minjoon and Wijerathne, Supun and Luciani, Aur{\'e}lien and Kandasaamy, Swaathi and Luo, Jie and Watkins, Xavier and Turner, Edd and Martin, Maria~J and {the UniProt Consortium} and Bateman, Alex and Martin, Maria-Jesus and Orchard, Sandra and Magrane, Michele and Ye, Shiqi and Adesina, Aduragbemi and Ahmad, Shadab and {Bowler-Barnett}, Emily H and Carpentier, David and Denny, Paul and Fan, Jun and Da Costa Gonzales, Leonardo Jose and Hussein, Abdulrahman and Ignatchenko, Alexandr and Insana, Giuseppe and Ishtiaq, Rizwan and Joshi, Vishal and Jyothi, Dushyanth and Kandasaamy, Swaathi and Lock, Antonia and Luciani, Aurelien and Luo, Jie and Lussi, Yvonne and Marin, Juan Sebastian Martinez and Raposo, Pedro and Rice, Daniel L and Stephenson, James and Totoo, Prabhat and Urakova, Nadya and Vasudev, Preethi and Wijerathne, Supun and Ibrahim, Khawaja Talal and Kim, Minjoon and Yu, Conny Wing-Heng and Bridge, Alan J and Aimo, Lucila and {Argoud-Puy}, Ghislaine and Auchincloss, Andrea H and Axelsen, Kristian B and Bansal, Parit and Baratin, Delphine and Batista Neto, Teresa M and Blatter, Marie-Claude and Bolleman, Jerven T and Boutet, Emmanuel and Breuza, Lionel and {Casals-Casas}, Cristina and Echioukh, Kamal Chikh and Coudert, Elisabeth and Cuche, Beatrice and De Castro, Edouard and Estreicher, Anne and Famiglietti, Maria L and Feuermann, Marc and Gasteiger, Elisabeth and Gaudet, Pascale and Gehant, Sebastien and Gerritsen, Vivienne and Gos, Arnaud and Gruaz, Nadine and Hulo, Chantal and {Hyka-Nouspikel}, Nevila and Jungo, Florence and Kerhornou, Arnaud and Mercier, Philippe Le and Lieberherr, Damien and Masson, Patrick and Morgat, Anne and Paesano, Salvo and Pedruzzi, Ivo and Pilbout, Sandrine and Pourcel, Lucille and Poux, Sylvain and Pozzato, Monica and Pruess, Manuela and Redaschi, Nicole and Rivoire, Catherine and Sigrist, Christian J A and Sonesson, Karin and Sundaram, Shyamala and Sveshnikova, Anastasia and Wu, Cathy H and Chen, Chuming and Huang, Hongzhan and Laiho, Kati and Lehvaslaiho, Minna and McGarvey, Peter and Natale, Darren A and Ross, Karen and Vinayaka, C R and Wang, Yuqi and Mazumber, Raja and Shanker, Vijay and Zhang, Jian}, + author = {Ahmad, Shadab and {Jose~da~Costa~Gonzales}, Leonardo and {Bowler-Barnett}, Emily~H and others}, year = 2025, month = jul, journal = {Nucleic Acids Research}, @@ -10,6 +10,8 @@ @article{ahmadUniProtWebsiteAPI2025 pages = {W547-W553}, issn = {0305-1048, 1362-4962}, doi = {10.1093/nar/gkaf394}, + pmid = {40331428}, + pmcid = {PMC12230682}, urldate = {2026-02-20}, abstract = {Abstract The UniProt REST API is a freely available, open-access resource that powers the UniProt.org website and gives users flexible programmatic interaction with protein knowledge data. It provides access to UniProtKB, UniRef, UniParc, Proteomes, GeneCentric, ARBA, UniRule, and the ID Mapping tool, along with supporting data and controlled vocabularies. Users can access the API with their favorite programming language and generate example code snippets to access the UniProt databases using the API documentation page (https://www.uniprot.org/api-documentation) in various languages. API results can be returned and downloaded in various formats. With an average of 303 million requests per month over the last year, the API enables structured search queries using logical operators and parentheses, allows users to specify fields of interest within results, and customize~downloads for direct integration into workflows. The API is a powerful tool that empowers users to fully utilize UniProt data across multiple datasets, enabling download, analysis, and extraction of valuable research insights. This website is free and open to all users, and there is no login requirement.}, copyright = {https://creativecommons.org/licenses/by/4.0/}, @@ -36,10 +38,41 @@ @article{bastianGephiOpenSource2009 file = {/home/glory/Zotero/storage/PQCTQ2HA/Bastian et al. - 2009 - Gephi An Open Source Software for Exploring and Manipulating Networks.pdf} } +@article{bavelasCommunicationPatternsTaskOriented1950a, + title = {Communication {{Patterns}} in {{Task-Oriented Groups}}}, + author = {Bavelas, Alex}, + year = 1950, + month = nov, + journal = {The Journal of the Acoustical Society of America}, + volume = {22}, + number = {6}, + pages = {725--730}, + issn = {0001-4966, 1520-8524}, + doi = {10.1121/1.1906679}, + urldate = {2026-06-02}, + langid = {english} +} + +@article{brinAnatomyLargescaleHypertextual1998a, + title = {The Anatomy of a Large-Scale Hypertextual {{Web}} Search Engine}, + author = {Brin, Sergey and Page, Lawrence}, + year = 1998, + month = apr, + journal = {Computer Networks and ISDN Systems}, + volume = {30}, + number = {1-7}, + pages = {107--117}, + issn = {01697552}, + doi = {10.1016/S0169-7552(98)00110-X}, + urldate = {2026-06-02}, + copyright = {https://www.elsevier.com/tdm/userlicense/1.0/}, + langid = {english} +} + @article{brownNAViGaTORNetworkAnalysis2009, title = {{{NAViGaTOR}}: {{Network Analysis}}, {{Visualization}} and {{Graphing Toronto}}}, shorttitle = {{{NAViGaTOR}}}, - author = {Brown, Kevin R. and Otasek, David and Ali, Muhammad and McGuffin, Michael J. and Xie, Wing and Devani, Baiju and van Toch, Ian Lawson and Jurisica, Igor}, + author = {Brown, Kevin R. and Otasek, David and Ali, Muhammad and others}, year = 2009, month = dec, journal = {Bioinformatics (Oxford, England)}, @@ -48,6 +81,8 @@ @article{brownNAViGaTORNetworkAnalysis2009 pages = {3327--3329}, issn = {1367-4811}, doi = {10.1093/bioinformatics/btp595}, + pmid = {19837718}, + pmcid = {PMC2788933}, abstract = {SUMMARY: NAViGaTOR is a powerful graphing application for the 2D and 3D visualization of biological networks. NAViGaTOR includes a rich suite of visual mark-up tools for manual and automated annotation, fast and scalable layout algorithms and OpenGL hardware acceleration to facilitate the visualization of large graphs. Publication-quality images can be rendered through SVG graphics export. NAViGaTOR supports community-developed data formats (PSI-XML, BioPax and GML), is platform-independent and is extensible through a plug-in architecture. AVAILABILITY: NAViGaTOR is freely available to the research community from http://ophid.utoronto.ca/navigator/. Installers and documentation are provided for 32- and 64-bit Windows, Mac, Linux and Unix. CONTACT: juris@ai.utoronto.ca SUPPLEMENTARY INFORMATION: Supplementary data are available at Bioinformatics online.}, langid = {english}, pmcid = {PMC2788933}, @@ -59,7 +94,7 @@ @ai.utoronto.ca @article{bunielloOpenTargetsPlatform2025, title = {Open {{Targets Platform}}: Facilitating Therapeutic Hypotheses Building in Drug Discovery}, shorttitle = {Open {{Targets Platform}}}, - author = {Buniello, Annalisa and Suveges, Daniel and {Cruz-Castillo}, Carlos and Llinares, Manuel~Bernal and Cornu, Helena and Lopez, Irene and Tsukanov, Kirill and {Rold{\'a}n-Romero}, Juan~Mar{\'i}a and Mehta, Chintan and Fumis, Luca and McNeill, Graham and Hayhurst, James~D and Martinez~Osorio, Ricardo~Esteban and Barkhordari, Ehsan and Ferrer, Javier and Carmona, Miguel and Uniyal, Prashant and Falaguera, Maria~J and Rusina, Polina and Smit, Ines and Schwartzentruber, Jeremy and Alegbe, Tobi and Ho, Vivien~W and Considine, Daniel and Ge, Xiangyu and Szyszkowski, Szymon and Tsepilov, Yakov and Ghoussaini, Maya and Dunham, Ian and Hulcoop, David~G and McDonagh, Ellen~M and Ochoa, David}, + author = {Buniello, Annalisa and Suveges, Daniel and {Cruz-Castillo}, Carlos and others}, year = 2025, month = jan, journal = {Nucleic Acids Research}, @@ -68,6 +103,8 @@ @article{bunielloOpenTargetsPlatform2025 pages = {D1467-D1475}, issn = {1362-4962}, doi = {10.1093/nar/gkae1128}, + pmid = {39657122}, + pmcid = {PMC11701534}, urldate = {2025-12-24}, abstract = {The Open Targets Platform (https://platform.opentargets.org) is a unique, open-source, publicly-available knowledge base providing data and tooling for systematic drug target identification, annotation, and prioritisation. Since our last report, we have expanded the scope of the Platform through a number of significant enhancements and data updates, with the aim to enable our users to formulate more flexible and impactful therapeutic hypotheses. In this context, we have completely revamped our target--disease associations page with more interactive facets and built-in functionalities to empower users with additional control over their experience using the Platform, and added a new Target Prioritisation view. This enables users to prioritise targets based upon clinical precedence, tractability, doability and safety attributes. We have also implemented a direction of effect assessment for eight sources of target--disease association evidence, showing the effect of genetic variation on the function of a target is associated with risk or protection for a trait to inform on potential mechanisms of modulation suitable for disease treatment. These enhancements and the introduction of new back and front-end technologies to support them have increased the impact and usability of our resource within the drug discovery community.}, file = {/home/glory/Zotero/storage/TCEG6G92/Buniello et al. - 2025 - Open Targets Platform facilitating therapeutic hypotheses building in drug discovery.pdf;/home/glory/Zotero/storage/KR9GR2NZ/gkae1128.html} @@ -102,7 +139,7 @@ @inproceedings{dangBioLinkerBottomupExploration2017 @article{duPINA30Mining2021, title = {{{PINA}} 3.0: Mining Cancer Interactome}, shorttitle = {{{PINA}} 3.0}, - author = {Du, Yang and Cai, Meng and Xing, Xiaofang and Ji, Jiafu and Yang, Ence and Wu, Jianmin}, + author = {Du, Yang and Cai, Meng and Xing, Xiaofang and others}, year = 2021, month = jan, journal = {Nucleic Acids Research}, @@ -111,6 +148,8 @@ @article{duPINA30Mining2021 pages = {D1351-D1357}, issn = {0305-1048}, doi = {10.1093/nar/gkaa1075}, + pmid = {33231689}, + pmcid = {PMC7779002}, urldate = {2025-05-14}, abstract = {Protein--protein interactions (PPIs) are crucial to mediate biological functions, and understanding PPIs in cancer type-specific context could help decipher the underlying molecular mechanisms of tumorigenesis and identify potential therapeutic options. Therefore, we update the Protein Interaction Network Analysis (PINA) platform to version 3.0, to integrate the unified human interactome with RNA-seq transcriptomes and mass spectrometry-based proteomes across tens of cancer types. A number of new analytical utilities were developed to help characterize the cancer context for a PPI network, which includes inferring proteins with expression specificity and identifying candidate prognosis biomarkers, putative cancer drivers, and therapeutic targets for a specific cancer type; as well as identifying pairs of co-expressing interacting proteins across cancer types. Furthermore, a brand-new web interface has been designed to integrate these new utilities within an interactive network visualization environment, which allows users to quickly and comprehensively investigate the roles of human interacting proteins in a cancer type-specific context. PINA is freely available at https://omics.bjcancer.org/pina/.}, file = {/home/glory/Zotero/storage/77F9IC72/Du et al. - 2021 - PINA 3.0 mining cancer interactome.pdf;/home/glory/Zotero/storage/K47UL6QQ/5999889.html} @@ -118,7 +157,7 @@ @article{duPINA30Mining2021 @article{ehlersIntroductionSurveyBiological2025, title = {An Introduction to and Survey of Biological Network Visualization}, - author = {Ehlers, Henry and Brich, Nicolas and Krone, Michael and N{\"o}llenburg, Martin and Yu, Jiacheng and Natsukawa, Hiroaki and Yuan, Xiaoru and Wu, Hsiang-Yun}, + author = {Ehlers, Henry and Brich, Nicolas and Krone, Michael and others}, year = 2025, month = feb, journal = {Computers \& Graphics}, @@ -132,7 +171,7 @@ @article{ehlersIntroductionSurveyBiological2025 @article{evgeniouMetaAnalysisHumanTranscriptomics2021, title = {A {{Meta-Analysis}} of {{Human Transcriptomics Data}} in the {{Context}} of {{Peritoneal Dialysis Identifies Novel Receptor-Ligand Interactions}} as {{Potential Therapeutic Targets}}}, - author = {Evgeniou, Michail and Sacnun, Juan Manuel and Kratochwill, Klaus and Perco, Paul}, + author = {Evgeniou, Michail and Sacnun, Juan Manuel and Kratochwill, Klaus and others}, year = 2021, month = dec, journal = {International Journal of Molecular Sciences}, @@ -141,6 +180,8 @@ @article{evgeniouMetaAnalysisHumanTranscriptomics2021 pages = {13277}, issn = {1422-0067}, doi = {10.3390/ijms222413277}, + pmid = {34948074}, + pmcid = {PMC8703997}, urldate = {2026-02-20}, abstract = {Peritoneal dialysis (PD) is one therapeutic option for patients with end-stage kidney disease (ESKD). Molecular profiling of samples from PD patients using different Omics technologies has led to the discovery of dysregulated molecular processes due to PD treatment in recent years. In particular, a number of transcriptomics (TX) datasets are currently available in the public domain in the context of PD. We set out to perform a meta-analysis of TX datasets to identify dysregulated receptor-ligand interactions in the context of PD-associated complications. We consolidated transcriptomics profiles from twelve untargeted genome-wide gene expression studies focusing on human cell cultures or samples from human PD patients. Gene set enrichment analysis was used to identify enriched biological processes. Receptor-ligand interactions were identified using data from CellPhoneDB. We identified 2591 unique differentially expressed genes in the twelve PD studies. Key enriched biological processes included angiogenesis, cell adhesion, extracellular matrix organization, and inflammatory response. We identified 70 receptor-ligand interaction pairs, with both interaction partners being dysregulated on the transcriptional level in one of the investigated tissues in the context of PD. Novel receptor-ligand interactions without prior annotation in the context of PD included BMPR2-GDF6, FZD4-WNT7B, ACKR2-CCL2, or the binding of EPGN and EREG to the EGFR, as well as the binding of SEMA6D to the receptors KDR and TYROBP. In summary, we have consolidated human transcriptomics datasets from twelve studies in the context of PD and identified sets of novel receptor-ligand pairs being dysregulated in the context of PD that warrant investigation in future functional studies.}, langid = {english}, @@ -157,6 +198,7 @@ @article{formicaMolecularPathwaysInvolved2020 pages = {109648}, issn = {08986568}, doi = {10.1016/j.cellsig.2020.109648}, + pmid = {32320858}, urldate = {2026-02-20}, langid = {english}, file = {/home/glory/Zotero/storage/VCQQG5LJ/Formica and Peters - 2020 - Molecular pathways involved in injury-repair and ADPKD progression.pdf} @@ -165,7 +207,7 @@ @article{formicaMolecularPathwaysInvolved2020 @article{freemanGraphiaPlatformGraphbased2022, title = {Graphia: {{A}} Platform for the Graph-Based Visualisation and Analysis of High Dimensional Data}, shorttitle = {Graphia}, - author = {Freeman, Tom C. and Horsewell, Sebastian and Patir, Anirudh and {Harling-Lee}, Josh and Regan, Tim and Shih, Barbara B. and Prendergast, James and Hume, David A. and Angus, Tim}, + author = {Freeman, Tom C. and Horsewell, Sebastian and Patir, Anirudh and others}, year = 2022, month = jul, journal = {PLoS Computational Biology}, @@ -174,6 +216,8 @@ @article{freemanGraphiaPlatformGraphbased2022 pages = {e1010310}, issn = {1553-734X}, doi = {10.1371/journal.pcbi.1010310}, + pmid = {35877685}, + pmcid = {PMC9352203}, urldate = {2025-05-23}, abstract = {Graphia is an open-source platform created for the graph-based analysis of the huge amounts of quantitative and qualitative data currently being generated from the study of genomes, genes, proteins metabolites and cells. Core to Graphia's functionality is support for the calculation of correlation matrices from any tabular matrix of continuous or discrete values, whereupon the software is designed to rapidly visualise the often very large graphs that result in 2D or 3D space. Following graph construction, an extensive range of measurement algorithms, routines for graph transformation, and options for the visualisation of node and edge attributes are available, for graph exploration and analysis. Combined, these provide a powerful solution for the interpretation of high-dimensional data from many sources, or data already in the form of a network or equivalent adjacency matrix. Several use cases of Graphia are described, to showcase its wide range of applications in the analysis biological data. Graphia runs on all major desktop operating systems, is extensible through the deployment of plugins and is freely available to download from https://graphia.app/., Graphia is a new visual analytics platform specifically created for the network-based analysis of large and complex data, such as that generated in huge amounts by modern biological analyses. It works in a data agnostic, hypothesis-free manner to generate correlation networks from any table of numerical or discrete values, thereafter providing a means to rapidly visualise the often very large networks that result, in either 2D or 3D space. Following network construction, the tool offers an extensive range of analysis algorithms, routines for network transformation, and options for the visualisation of metadata. This provides a powerful analysis solution for the exploration and interpretation of high-dimensional data from any source, as well as any data already defined as a network. Several use cases of Graphia are described to showcase its wide range of applications in the analysis biological data. Graphia is open source and free to all.}, pmcid = {PMC9352203}, @@ -181,9 +225,25 @@ @article{freemanGraphiaPlatformGraphbased2022 file = {/home/glory/Zotero/storage/TQIDVB9B/Freeman et al. - 2022 - Graphia A platform for the graph-based visualisation and analysis of high dimensional data.pdf} } +@article{freemanSetMeasuresCentrality1977a, + title = {A {{Set}} of {{Measures}} of {{Centrality Based}} on {{Betweenness}}}, + author = {Freeman, Linton C.}, + year = 1977, + month = mar, + journal = {Sociometry}, + volume = {40}, + number = {1}, + eprint = {3033543}, + eprinttype = {jstor}, + pages = {35}, + issn = {00380431}, + doi = {10.2307/3033543}, + urldate = {2026-06-02} +} + @article{gebeshuberComputationalDrugRepositioning2023, title = {Computational Drug Repositioning of Clopidogrel as a Novel Therapeutic Option for Focal Segmental Glomerulosclerosis}, - author = {Gebeshuber, Christoph A. and {Daniel-Fischer}, Lisa and Regele, Heinz and Schachner, Helga and Aufricht, Christoph and Kornauth, Christoph and Ley, Matthias and Alper, Seth L. and Herzog, Rebecca and Kratochwill, Klaus and Perco, Paul}, + author = {Gebeshuber, Christoph A. and {Daniel-Fischer}, Lisa and Regele, Heinz and others}, year = 2023, month = sep, journal = {Translational Research}, @@ -191,6 +251,7 @@ @article{gebeshuberComputationalDrugRepositioning2023 pages = {28--34}, issn = {1931-5244}, doi = {10.1016/j.trsl.2023.04.001}, + pmid = {37059330}, urldate = {2025-05-14}, abstract = {Focal segmental glomerulosclerosis (FSGS) is a glomerular lesion often associated with nephrotic syndrome. It is also associated with a high risk of progression to end-stage kidney disease. Current treatment of FSGS is limited to systemic corticosteroids or calcineurin inhibition, along with inhibitors of the renin-angiotensin-aldosterone system. FSGS is heterogeneous in etiology, and novel therapies targeting specific, dysregulated molecular pathways represent a major unmet medical need. We have generated a network-based molecular model of FSGS pathophysiology using previously established systems biology workflows to allow computational evaluation of compounds for their predicted interference with molecular processes contributing to FSGS. We identified the anti-platelet drug clopidogrel as a therapeutic option to counterbalance dysregulated FSGS pathways. This prediction of our computational screen was validated by testing clopidogrel in the adriamycin FSGS mouse model. Clopidogrel improved key FSGS outcome parameters and significantly reduced urinary albumin to creatinine ratio (P {$<$} 0.01) and weight loss (P {$<$} 0.01), and ameliorated histopathological damage (P {$<$} 0.05). Clopidogrel is used to treat several cardiovascular diseases linked to chronic kidney disease. Clopidogrel's favorable safety profile and its efficacy in the adriamycin mouse FSGS model thus recommend it as an attractive drug repositioning candidate for clinical trial in FSGS.}, file = {/home/glory/Zotero/storage/AVVUGEEP/Gebeshuber et al. - 2023 - Computational drug repositioning of clopidogrel as a novel therapeutic option for focal segmental gl.pdf;/home/glory/Zotero/storage/LW7PX9M2/S1931524423000579.html} @@ -198,13 +259,14 @@ @article{gebeshuberComputationalDrugRepositioning2023 @misc{jacomyGephiGephilite2025, title = {Gephi/Gephi-Lite}, - author = {Jacomy, Mathieu and Jacomy, Alexis and Girard, Paul and Simard, Beno{\^i}t}, + author = {Jacomy, Mathieu and Jacomy, Alexis and Girard, Paul and others}, year = 2025, month = may, + publisher = {Gephi}, urldate = {2025-05-26}, abstract = {A web-based, lighter version of Gephi}, copyright = {GPL-3.0}, - howpublished = {Gephi}, + howpublished = {https://github.com/gephi/gephi-lite}, keywords = {dataviz,network-analysis} } @@ -221,7 +283,7 @@ @misc{jacomyJacomyalSigmajs2025 @article{karlssonSingleCellType2021, title = {A Single--Cell Type Transcriptomics Map of Human Tissues}, - author = {Karlsson, Max and Zhang, Cheng and M{\'e}ar, Loren and Zhong, Wen and Digre, Andreas and Katona, Borbala and Sj{\"o}stedt, Evelina and Butler, Lynn and Odeberg, Jacob and Dusart, Philip and Edfors, Fredrik and Oksvold, Per and Von Feilitzen, Kalle and Zwahlen, Martin and Arif, Muhammad and Altay, Ozlem and Li, Xiangyu and Ozcan, Mehmet and Mardinoglu, Adil and Fagerberg, Linn and Mulder, Jan and Luo, Yonglun and Ponten, Fredrik and Uhl{\'e}n, Mathias and Lindskog, Cecilia}, + author = {Karlsson, Max and Zhang, Cheng and M{\'e}ar, Loren and others}, year = 2021, month = jul, journal = {Science Advances}, @@ -230,6 +292,8 @@ @article{karlssonSingleCellType2021 pages = {eabh2169}, issn = {2375-2548}, doi = {10.1126/sciadv.abh2169}, + pmid = {34321199}, + pmcid = {PMC8318366}, urldate = {2026-02-20}, abstract = {Single-cell RNA analysis has been integrated with spatial protein profiling to create a single--cell type map of human tissues. , Advances in molecular profiling have opened up the possibility to map the expression of genes in cells, tissues, and organs in the human body. Here, we combined single-cell transcriptomics analysis with spatial antibody-based protein profiling to create a high-resolution single--cell type map of human tissues. An open access atlas has been launched to allow researchers to explore the expression of human protein-coding genes in 192 individual cell type clusters. An expression specificity classification was performed to determine the number of genes elevated in each cell type, allowing comparisons with bulk transcriptomics data. The analysis highlights distinct expression clusters corresponding to cell types sharing similar functions, both within the same organs and between organs.}, copyright = {https://creativecommons.org/licenses/by-nc/4.0/}, @@ -240,7 +304,7 @@ @article{karlssonSingleCellType2021 @article{liuPaintOmics4New2022, title = {{{PaintOmics}} 4: New Tools for the Integrative Analysis of Multi-Omics Datasets Supported by Multiple Pathway Databases}, shorttitle = {{{PaintOmics}} 4}, - author = {Liu, Tianyuan and Salguero, Pedro and Petek, Marko and {Martinez-Mira}, Carlos and {Balzano-Nogueira}, Leandro and Ram{\v s}ak, {\v Z}iva and McIntyre, Lauren and Gruden, Kristina and Tarazona, Sonia and Conesa, Ana}, + author = {Liu, Tianyuan and Salguero, Pedro and Petek, Marko and others}, year = 2022, month = jul, journal = {Nucleic Acids Research}, @@ -249,6 +313,8 @@ @article{liuPaintOmics4New2022 pages = {W551-W559}, issn = {0305-1048}, doi = {10.1093/nar/gkac352}, + pmid = {35609982}, + pmcid = {PMC9252773}, urldate = {2025-05-14}, abstract = {PaintOmics is a web server for the integrative analysis and visualisation of multi-omics datasets using biological pathway maps. PaintOmics 4 has several notable updates that improve and extend analyses. Three pathway databases are now supported: KEGG, Reactome and MapMan, providing more comprehensive pathway knowledge for animals and plants. New metabolite analysis methods fill gaps in traditional pathway-based enrichment methods. The metabolite hub analysis selects compounds with a high number of significant genes in their neighbouring network, suggesting regulation by gene expression changes. The metabolite class activity analysis tests the hypothesis that a metabolic class has a higher-than-expected proportion of significant elements, indicating that these compounds are regulated in the experiment. Finally, PaintOmics 4 includes a regulatory omics module to analyse the contribution of trans-regulatory layers (microRNA and transcription factors, RNA-binding proteins) to regulate pathways. We show the performance of PaintOmics 4 on both mouse and plant data to highlight how these new analysis features provide novel insights into regulatory biology. PaintOmics 4 is available at https://paintomics.org/.}, file = {/home/glory/Zotero/storage/45Q2VGT4/Liu et al. - 2022 - PaintOmics 4 new tools for the integrative analysis of multi-omics datasets supported by multiple p.pdf;/home/glory/Zotero/storage/9MTJAQDX/6591534.html} @@ -256,7 +322,7 @@ @article{liuPaintOmics4New2022 @article{milacicReactomePathwayKnowledgebase2024, title = {The {{Reactome Pathway Knowledgebase}} 2024}, - author = {Milacic, Marija and Beavers, Deidre and Conley, Patrick and Gong, Chuqiao and Gillespie, Marc and Griss, Johannes and Haw, Robin and Jassal, Bijay and Matthews, Lisa and May, Bruce and Petryszak, Robert and Ragueneau, Eliot and Rothfels, Karen and Sevilla, Cristoffer and Shamovsky, Veronica and Stephan, Ralf and Tiwari, Krishna and Varusai, Thawfeek and Weiser, Joel and Wright, Adam and Wu, Guanming and Stein, Lincoln and Hermjakob, Henning and D'Eustachio, Peter}, + author = {Milacic, Marija and Beavers, Deidre and Conley, Patrick and others}, year = 2024, month = jan, journal = {Nucleic Acids Research}, @@ -265,6 +331,8 @@ @article{milacicReactomePathwayKnowledgebase2024 pages = {D672-D678}, issn = {0305-1048}, doi = {10.1093/nar/gkad1025}, + pmid = {37941124}, + pmcid = {PMC10767911}, urldate = {2025-12-22}, abstract = {The Reactome Knowledgebase (https://reactome.org), an Elixir and GCBR core biological data resource, provides manually curated molecular details of a broad range of normal and disease-related biological processes. Processes are annotated as an ordered network of molecular transformations in a single consistent data model. Reactome thus functions both as a digital archive of manually curated human biological processes and as a tool for discovering functional relationships in data such as gene expression profiles or somatic mutation catalogs from tumor cells. Here we review progress towards annotation of the entire human proteome, targeted annotation of disease-causing genetic variants of proteins and of small-molecule drugs in a pathway context, and towards supporting explicit annotation of cell- and tissue-specific pathways. Finally, we briefly discuss issues involved in making Reactome more fully interoperable with other related resources such as the Gene Ontology and maintaining the resulting community resource network.}, file = {/home/glory/Zotero/storage/5C5XHSGM/Milacic et al. - 2024 - The Reactome Pathway Knowledgebase 2024.pdf;/home/glory/Zotero/storage/2T6J848L/gkad1025.html} @@ -272,7 +340,7 @@ @article{milacicReactomePathwayKnowledgebase2024 @article{mutoDefiningCellularComplexity2022, title = {Defining Cellular Complexity in Human Autosomal Dominant Polycystic Kidney Disease by Multimodal Single Cell Analysis}, - author = {Muto, Yoshiharu and Dixon, Eryn E. and Yoshimura, Yasuhiro and Wu, Haojia and Omachi, Kohei and Ledru, Nicolas and Wilson, Parker C. and King, Andrew J. and Eric Olson, N. and Gunawan, Marvin G. and Kuo, Jay J. and Cox, Jennifer H. and Miner, Jeffrey H. and Seliger, Stephen L. and Woodward, Owen M. and Welling, Paul A. and Watnick, Terry J. and Humphreys, Benjamin D.}, + author = {Muto, Yoshiharu and Dixon, Eryn E. and Yoshimura, Yasuhiro and others}, year = 2022, month = oct, journal = {Nature Communications}, @@ -281,16 +349,35 @@ @article{mutoDefiningCellularComplexity2022 pages = {6497}, issn = {2041-1723}, doi = {10.1038/s41467-022-34255-z}, + pmid = {36310237}, + pmcid = {PMC9618568}, urldate = {2026-02-20}, abstract = {Abstract Autosomal dominant polycystic kidney disease (ADPKD) is the leading genetic cause of end stage renal disease characterized by progressive expansion of kidney cysts. To better understand the cell types and states driving ADPKD progression, we analyze eight ADPKD and five healthy human kidney samples, generating single cell multiomic atlas consisting of \textasciitilde 100,000 single nucleus transcriptomes and \textasciitilde 50,000 single nucleus epigenomes. Activation of proinflammatory, profibrotic signaling pathways are driven by proximal tubular cells with a failed repair transcriptomic signature, proinflammatory fibroblasts and collecting duct cells. We identify GPRC5A as a marker for cyst-lining collecting duct cells that exhibits increased transcription factor binding motif availability for NF-{$\kappa$}B, TEAD, CREB and retinoic acid receptors. We identify and validate a distal enhancer regulating GPRC5A expression containing these motifs. This single cell multiomic analysis of human ADPKD reveals previously unrecognized cellular heterogeneity and provides a foundation to develop better diagnostic and therapeutic approaches.}, langid = {english}, file = {/home/glory/Zotero/storage/H4AM7NVE/Muto et al. - 2022 - Defining cellular complexity in human autosomal dominant polycystic kidney disease by multimodal sin.pdf} } +@inbook{newmanMathematicsNetworks2018a, + title = {Mathematics of Networks}, + booktitle = {Networks}, + author = {Newman, Mark}, + year = 2018, + month = jul, + edition = {2}, + pages = {104--157}, + publisher = {Oxford University PressOxford}, + doi = {10.1093/oso/9780198805090.003.0006}, + urldate = {2026-06-02}, + abstract = {Abstract An introduction to the mathematical tools used in the study of networks. Topics discussed include: the adjacency matrix; weighted, directed, acyclic, and bipartite networks; multilayer and dynamic networks; trees; planar networks. Some basic properties of networks are then discussed, including degrees, density and sparsity, paths on networks, component structure, and connectivity and cut sets. The final part of the chapter focuses on the graph Laplacian and its applications to network visualization, graph partitioning, the theory of random walks, and other problems.}, + collaborator = {Newman, Mark}, + isbn = {978-0-19-880509-0 978-0-19-184323-5}, + langid = {english} +} + @article{onoCytoscapeWebBringing2025, title = {Cytoscape {{Web}}: Bringing Network Biology to the Browser}, shorttitle = {Cytoscape {{Web}}}, - author = {Ono, Keiichiro and Fong, Dylan and Gao, Chengzhan and Churas, Christopher and Pillich, Rudolf and Lenkiewicz, Joanna and Pratt, Dexter and Pico, Alexander~R and Hanspers, Kristina and Xin, Yihang and Morris, John and Kucera, Mike and Franz, Max and Lopes, Christian and Bader, Gary and Ideker, Trey and Chen, Jing}, + author = {Ono, Keiichiro and Fong, Dylan and Gao, Chengzhan and others}, year = 2025, month = jul, journal = {Nucleic Acids Research}, @@ -299,6 +386,8 @@ @article{onoCytoscapeWebBringing2025 pages = {W203-W212}, issn = {0305-1048, 1362-4962}, doi = {10.1093/nar/gkaf365}, + pmid = {40308211}, + pmcid = {PMC12230733}, urldate = {2026-02-20}, abstract = {Abstract Since its introduction in 2003, Cytoscape has been a de facto standard for visualizing and analyzing biological networks. We now introduce Cytoscape Web (https://web.cytoscape.org), an online implementation that captures the interface and key visualization functionality of the desktop while providing integration with web tools and databases. Cytoscape Web enhances accessibility, simplifying collaboration through online data sharing. It integrates with Cytoscape desktop via the CX2 network exchange format and with the Network Data Exchange~for storing and sharing networks. The platform supports extensibility through an App framework for UI components and Service Apps for algorithm integration, fostering community-driven development of new analysis tools. Overall, Cytoscape Web enhances network biology by providing a versatile, accessible, and collaborative online platform that adapts to evolving computational challenges, laying a foundation for future incorporation of advanced network analysis capabilities by the community.}, copyright = {https://creativecommons.org/licenses/by/4.0/}, @@ -307,7 +396,7 @@ @article{onoCytoscapeWebBringing2025 @article{orchardMIntActProjectIntAct2014, title = {The {{MIntAct}} Project---{{IntAct}} as a Common Curation Platform for 11 Molecular Interaction Databases}, - author = {Orchard, Sandra and Ammari, Mais and Aranda, Bruno and Breuza, Lionel and Briganti, Leonardo and {Broackes-Carter}, Fiona and Campbell, Nancy H. and Chavali, Gayatri and Chen, Carol and {del-Toro}, Noemi and Duesbury, Margaret and Dumousseau, Marine and Galeota, Eugenia and Hinz, Ursula and Iannuccelli, Marta and Jagannathan, Sruthi and Jimenez, Rafael and Khadake, Jyoti and Lagreid, Astrid and Licata, Luana and Lovering, Ruth C. and Meldal, Birgit and Melidoni, Anna N. and Milagros, Mila and Peluso, Daniele and Perfetto, Livia and Porras, Pablo and Raghunath, Arathi and {Ricard-Blum}, Sylvie and Roechert, Bernd and Stutz, Andre and Tognolli, Michael and Van Roey, Kim and Cesareni, Gianni and Hermjakob, Henning}, + author = {Orchard, Sandra and Ammari, Mais and Aranda, Bruno and others}, year = 2014, month = jan, journal = {Nucleic Acids Research}, @@ -316,6 +405,8 @@ @article{orchardMIntActProjectIntAct2014 pages = {D358-D363}, issn = {0305-1048, 1362-4962}, doi = {10.1093/nar/gkt1115}, + pmid = {24234451}, + pmcid = {PMC3965093}, urldate = {2026-02-20}, copyright = {http://creativecommons.org/licenses/by/3.0/}, langid = {english}, @@ -325,7 +416,7 @@ @article{orchardMIntActProjectIntAct2014 @article{oughtredBioGRIDDatabaseComprehensive2021, title = {The {{BioGRID}} Database: {{A}} Comprehensive Biomedical Resource of Curated Protein, Genetic, and Chemical Interactions}, shorttitle = {The {{BioGRID}} Database}, - author = {Oughtred, Rose and Rust, Jennifer and Chang, Christie and Breitkreutz, Bobby-Joe and Stark, Chris and Willems, Andrew and Boucher, Lorrie and Leung, Genie and Kolas, Nadine and Zhang, Frederick and Dolma, Sonam and {Coulombe-Huntington}, Jasmin and {Chatr-Aryamontri}, Andrew and Dolinski, Kara and Tyers, Mike}, + author = {Oughtred, Rose and Rust, Jennifer and Chang, Christie and others}, year = 2021, month = jan, journal = {Protein Science: A Publication of the Protein Society}, @@ -334,6 +425,8 @@ @article{oughtredBioGRIDDatabaseComprehensive2021 pages = {187--200}, issn = {1469-896X}, doi = {10.1002/pro.3978}, + pmid = {33070389}, + pmcid = {PMC7737760}, abstract = {The BioGRID (Biological General Repository for Interaction Datasets, thebiogrid.org) is an open-access database resource that houses manually curated protein and genetic interactions from multiple species including yeast, worm, fly, mouse, and human. The \textasciitilde 1.93 million curated interactions in BioGRID can be used to build complex networks to facilitate biomedical discoveries, particularly as related to human health and disease. All BioGRID content is curated from primary experimental evidence in the biomedical literature, and includes both focused low-throughput studies and large high-throughput datasets. BioGRID also captures protein post-translational modifications and protein or gene interactions with bioactive small molecules including many known drugs. A built-in network visualization tool combines all annotations and allows users to generate network graphs of protein, genetic and chemical interactions. In addition to general curation across species, BioGRID undertakes themed curation projects in specific aspects of cellular regulation, for example the ubiquitin-proteasome system, as well as specific disease areas, such as for the SARS-CoV-2 virus that causes COVID-19 severe acute respiratory syndrome. A recent extension of BioGRID, named the Open Repository of CRISPR Screens (ORCS, orcs.thebiogrid.org), captures single mutant phenotypes and genetic interactions from published high throughput genome-wide CRISPR/Cas9-based genetic screens. BioGRID-ORCS contains datasets for over 1,042 CRISPR screens carried out to date in human, mouse and fly cell lines. The biomedical research community can freely access all BioGRID data through the web interface, standardized file downloads, or via model organism databases and partner meta-databases.}, langid = {english}, pmcid = {PMC7737760}, @@ -345,7 +438,7 @@ @article{oughtredBioGRIDDatabaseComprehensive2021 @article{shannonCytoscapeSoftwareEnvironment2003, title = {Cytoscape: {{A Software Environment}} for {{Integrated Models}} of {{Biomolecular Interaction Networks}}}, shorttitle = {Cytoscape}, - author = {Shannon, Paul and Markiel, Andrew and Ozier, Owen and Baliga, Nitin S. and Wang, Jonathan T. and Ramage, Daniel and Amin, Nada and Schwikowski, Benno and Ideker, Trey}, + author = {Shannon, Paul and Markiel, Andrew and Ozier, Owen and others}, year = 2003, month = jan, journal = {Genome Research}, @@ -355,6 +448,8 @@ @article{shannonCytoscapeSoftwareEnvironment2003 publisher = {Cold Spring Harbor Lab}, issn = {1088-9051, 1549-5469}, doi = {10.1101/gr.1239303}, + pmid = {14597658}, + pmcid = {PMC403769}, urldate = {2025-05-14}, abstract = {Cytoscape is an open source software project for integrating biomolecular interaction networks with high-throughput expression data and other molecular states into a unified conceptual framework. Although applicable to any system of molecular components and interactions, Cytoscape is most powerful when used in conjunction with large databases of protein-protein, protein-DNA, and genetic interactions that are increasingly available for humans and model organisms. Cytoscape's software Core provides basic functionality to layout and query the network; to visually integrate the network with expression profiles, phenotypes, and other molecular states; and to link the network to databases of functional annotations. The Core is extensible through a straightforward plug-in architecture, allowing rapid development of additional computational analyses and features. Several case studies of Cytoscape plug-ins are surveyed, including a search for interaction pathways correlating with changes in gene expression, a study of protein complexes involved in cellular recovery to DNA damage, inference of a combined physical/functional interaction network for Halobacterium, and an interface to detailed stochastic/kinetic gene regulatory models.}, langid = {english}, @@ -365,7 +460,7 @@ @article{shannonCytoscapeSoftwareEnvironment2003 @article{songSystemsBiologyAutosomal2009, title = {Systems Biology of Autosomal Dominant Polycystic Kidney Disease ({{ADPKD}}): Computational Identification of Gene Expression Pathways and Integrated Regulatory Networks}, shorttitle = {Systems Biology of Autosomal Dominant Polycystic Kidney Disease ({{ADPKD}})}, - author = {Song, Xuewen and Di Giovanni, Valeria and He, Ning and Wang, Kairong and Ingram, Alistair and Rosenblum, Norman D. and Pei, York}, + author = {Song, Xuewen and Di Giovanni, Valeria and He, Ning and others}, year = 2009, month = jul, journal = {Human Molecular Genetics}, @@ -374,14 +469,33 @@ @article{songSystemsBiologyAutosomal2009 pages = {2328--2343}, issn = {1460-2083, 0964-6906}, doi = {10.1093/hmg/ddp165}, + pmid = {19346236}, urldate = {2026-02-20}, langid = {english} } +@article{stanleySecondaryAnalysisConcurrent2024, + title = {A Secondary Analysis of Concurrent Use of Metformin and Tolvaptan in {{ADPKD}} Tolvaptan Trials}, + author = {Stanley, I. Kitty and Palma, Anton M. and Viecelli, Andrea K. and others}, + year = 2024, + month = mar, + journal = {Journal of Nephrology}, + volume = {37}, + number = {5}, + pages = {1417-1419}, + issn = {1724-6059}, + doi = {10.1007/s40620-024-01906-x}, + pmid = {38512373}, + pmcid = {PMC11405464}, + urldate = {2026-06-29}, + langid = {english}, + file = {/home/glory/Zotero/storage/X9J259RY/Stanley et al. - 2024 - A secondary analysis of concurrent use of metformin and tolvaptan in ADPKD tolvaptan trials.pdf} +} + @article{szklarczykSTRINGDatabase20252025, title = {The {{STRING}} Database in 2025: Protein Networks with Directionality of Regulation}, shorttitle = {The {{STRING}} Database in 2025}, - author = {Szklarczyk, Damian and Nastou, Katerina and Koutrouli, Mikaela and Kirsch, Rebecca and Mehryary, Farrokh and Hachilif, Radja and Hu, Dewei and Peluso, Matteo E and Huang, Qingyao and Fang, Tao and Doncheva, Nadezhda T and Pyysalo, Sampo and Bork, Peer and Jensen, Lars J and {von~Mering}, Christian}, + author = {Szklarczyk, Damian and Nastou, Katerina and Koutrouli, Mikaela and others}, year = 2025, month = jan, journal = {Nucleic Acids Research}, @@ -390,14 +504,34 @@ @article{szklarczykSTRINGDatabase20252025 pages = {D730-D737}, issn = {1362-4962}, doi = {10.1093/nar/gkae1113}, + pmid = {39558183}, + pmcid = {PMC11701646}, urldate = {2025-05-14}, abstract = {Proteins cooperate, regulate and bind each other to achieve their functions. Understanding the complex network of their interactions is essential for a systems-level description of cellular processes. The STRING database compiles, scores and integrates protein--protein association information drawn from experimental assays, computational predictions and prior knowledge. Its goal is to create comprehensive and objective global networks that encompass both physical and functional interactions. Additionally, STRING provides supplementary tools such as network clustering and pathway enrichment analysis. The latest version, STRING 12.5, introduces a new `regulatory network', for which it gathers evidence on the type and directionality of interactions using curated pathway databases and a fine-tuned language model parsing the literature. This update enables users to visualize and access three distinct network types---functional, physical and regulatory---separately, each applicable to distinct research needs. In addition, the pathway enrichment detection functionality has been updated, with better false discovery rate corrections, redundancy filtering~and improved visual displays. The resource now also offers improved annotations of clustered networks and provides users with downloadable network embeddings, which facilitate the use of STRING networks in machine learning and allow cross-species transfer of protein information. The STRING database is available online at https://string-db.org/.}, file = {/home/glory/Zotero/storage/H9QB4EM4/Szklarczyk et al. - 2025 - The STRING database in 2025 protein networks with directionality of regulation.pdf;/home/glory/Zotero/storage/2FMHRD87/7903368.html} } +@misc{teamQwen35OmniTechnicalReport2026, + title = {Qwen3.5-{{Omni Technical Report}}}, + author = {{Qwen Team}}, + howpublished = {Preprint at arXiv:2604.15804}, + year = 2026, + month = apr, + number = {arXiv:2604.15804}, + eprint = {2604.15804}, + primaryclass = {cs.CL}, + publisher = {arXiv}, + doi = {10.48550/arXiv.2604.15804}, + urldate = {2026-05-29}, + abstract = {In this work, we present Qwen3.5-Omni, the latest advancement in the Qwen-Omni model family. Representing a significant evolution over its predecessor, Qwen3.5-Omni scales to hundreds of billions of parameters and supports a 256k context length. By leveraging a massive dataset comprising heterogeneous text-vision pairs and over 100 million hours of audio-visual content, the model demonstrates robust omni-modality capabilities. Qwen3.5-Omni-plus achieves SOTA results across 215 audio and audio-visual understanding, reasoning, and interaction subtasks and benchmarks, surpassing Gemini-3.1 Pro in key audio tasks and matching it in comprehensive audio-visual understanding. Architecturally, Qwen3.5-Omni employs a Hybrid Attention Mixture-of-Experts (MoE) framework for both Thinker and Talker, enabling efficient long-sequence inference. The model facilitates sophisticated interaction, supporting over 10 hours of audio understanding and 400 seconds of 720P video (at 1 FPS). To address the inherent instability and unnaturalness in streaming speech synthesis, often caused by encoding efficiency discrepancies between text and speech tokenizers, we introduce ARIA. ARIA dynamically aligns text and speech units, significantly enhancing the stability and prosody of conversational speech with minimal latency impact. Furthermore, Qwen3.5-Omni expands linguistic boundaries, supporting multilingual understanding and speech generation across 10 languages with human-like emotional nuance. Finally, Qwen3.5-Omni exhibits superior audio-visual grounding capabilities, generating script-level structured captions with precise temporal synchronization and automated scene segmentation. Remarkably, we observed the emergence of a new capability in omnimodal models: directly performing coding based on audio-visual instructions, which we call Audio-Visual Vibe Coding.}, + archiveprefix = {arXiv}, + keywords = {Computer Science - Computation and Language,Electrical Engineering and Systems Science - Audio and Speech Processing}, + file = {/home/glory/Zotero/storage/VLJX4DAX/Qwen Team - 2026 - Qwen3.5-Omni Technical Report.pdf;/home/glory/Zotero/storage/UI79QTGZ/2604.html} +} + @article{valenzuela-escarcegaLargescaleAutomatedMachine2018, title = {Large-Scale Automated Machine Reading Discovers New Cancer-Driving Mechanisms}, - author = {{Valenzuela-Esc{\'a}rcega}, Marco A and Babur, {\"O}zg{\"u}n and {Hahn-Powell}, Gus and Bell, Dane and Hicks, Thomas and {Noriega-Atala}, Enrique and Wang, Xia and Surdeanu, Mihai and Demir, Emek and Morrison, Clayton T}, + author = {{Valenzuela-Esc{\'a}rcega}, Marco A and Babur, {\"O}zg{\"u}n and {Hahn-Powell}, Gus and others}, year = 2018, month = jan, journal = {Database}, @@ -405,6 +539,8 @@ @article{valenzuela-escarcegaLargescaleAutomatedMachine2018 pages = {bay098}, issn = {1758-0463}, doi = {10.1093/database/bay098}, + pmid = {30256986}, + pmcid = {PMC6156821}, urldate = {2025-05-26}, abstract = {PubMed, a repository and search engine for biomedical literature, now indexes \>1 million articles each year. This exceeds the processing capacity of human domain experts, limiting our ability to truly understand many diseases. We present Reach, a system for automated, large-scale machine reading of biomedical papers that can extract mechanistic descriptions of biological processes with relatively high precision at high throughput. We demonstrate that combining the extracted pathway fragments with existing biological data analysis algorithms that rely on curated models helps identify and explain a large number of previously unidentified mutually exclusive altered signaling pathways in seven different cancer types. This work shows that combining human-curated `big mechanisms' with extracted `big data' can lead to a causal, predictive understanding of cellular processes and unlock important downstream applications.}, keywords = {REACH;biolinker}, @@ -414,7 +550,7 @@ @article{valenzuela-escarcegaLargescaleAutomatedMachine2018 @article{wangG6WebbasedLibrary2021, title = {G6: {{A}} Web-Based Library for Graph Visualization}, shorttitle = {G6}, - author = {Wang, Yanyan and Bai, Zhanning and Lin, Zhifeng and Dong, Xiaoqing and Feng, Yingchaojie and Pan, Jiacheng and Chen, Wei}, + author = {Wang, Yanyan and Bai, Zhanning and Lin, Zhifeng and others}, year = 2021, month = dec, journal = {Visual Informatics}, @@ -432,7 +568,7 @@ @article{wangG6WebbasedLibrary2021 @article{zhouOmicsNet20Webbased2022, title = {{{OmicsNet}} 2.0: A Web-Based Platform for Multi-Omics Integration and Network Visual Analytics}, shorttitle = {{{OmicsNet}} 2.0}, - author = {Zhou, Guangyan and Pang, Zhiqiang and Lu, Yao and Ewald, Jessica and Xia, Jianguo}, + author = {Zhou, Guangyan and Pang, Zhiqiang and Lu, Yao and others}, year = 2022, month = jul, journal = {Nucleic Acids Research}, @@ -441,6 +577,8 @@ @article{zhouOmicsNet20Webbased2022 pages = {W527-W533}, issn = {0305-1048}, doi = {10.1093/nar/gkac376}, + pmid = {35639733}, + pmcid = {PMC9252810}, urldate = {2025-05-14}, abstract = {Researchers are increasingly seeking to interpret molecular data within a multi-omics context to gain a more comprehensive picture of their study system. OmicsNet (www.omicsnet.ca) is a web-based tool developed to allow users to easily build, visualize, and analyze multi-omics networks to study rich relationships among lists of `omics features of interest. Three major improvements have been introduced in OmicsNet 2.0, which include: (i) enhanced network visual analytics with eleven 2D graph layout options and a novel 3D module layout; (ii) support for three new `omics types: single nucleotide polymorphism (SNP) list from genetic variation studies; taxon list from microbiome profiling studies, as well as liquid chromatography--mass spectrometry (LC--MS) peaks from~untargeted metabolomics;~and (iii) measures to improve research reproducibility by coupling R command history with the release of the companion OmicsNetR package, and generation of persistent links to share interactive network views. We performed a case study using the multi-omics data obtained from a recent large-scale investigation~on inflammatory bowel disease (IBD) and demonstrated that OmicsNet was able to quickly create meaningful multi-omics context to facilitate hypothesis generation and mechanistic insights.}, file = {/home/glory/Zotero/storage/V6KTIYDY/Zhou et al. - 2022 - OmicsNet 2.0 a web-based platform for multi-omics integration and network visual analytics.pdf;/home/glory/Zotero/storage/WUPC57XE/6593602.html} diff --git a/manuscript/shared/Fig/main-figure.png b/manuscript/shared/Fig/main-figure.png deleted file mode 100644 index 1c10797..0000000 Binary files a/manuscript/shared/Fig/main-figure.png and /dev/null differ diff --git a/manuscript/shared/content.tex b/manuscript/shared/content.tex deleted file mode 100644 index d9a4ccd..0000000 --- a/manuscript/shared/content.tex +++ /dev/null @@ -1,94 +0,0 @@ -% shared/content.tex - Body sections shared across all venue flavors. -% Include this from each venue's main.tex after \maketitle. -% -% Convention: venue-specific tweaks use \ifbiorxiv (defined in each main.tex). - -\section{Introduction} -Visualization of biological networks is a core technique in systems biology for representation and analysis of complex, often heterogeneous, data. Network visualization combined with quantitative graph analysis such as detection of network modules, hub or bottleneck nodes, or cross-talk between networks can lead to novel hypothesis generation or detection of relevant patterns in biomedical data \citep{ehlersIntroductionSurveyBiological2025}. Biological networks are of particular relevance in digital drug discovery and drug target prioritization and are used in modeling disease pathobiology as well as drug mechanism of action on a molecular level. Biological networks also represent an intuitive way of communicating and sharing research findings with collaborators or disseminating them to the broader scientific community. - -Cytoscape and Gephi are considered gold standards for interactive network visualization with an exhaustive plugin ecosystem and rich layout capabilities, but are desktop-focused and lack portability or modern web-app features \citep{shannonCytoscapeSoftwareEnvironment2003,bastianGephiOpenSource2009}. Other tools such as STRING or OmicsNet 2.0 prioritize data integration, enrichment analysis, and access to extensive biological databases over visualization and customization, integrating the network viewer as a helper and not as the focus of the application \citep{szklarczykSTRINGDatabase20252025,zhouOmicsNet20Webbased2022}. Several web-based alternatives have emerged to address portability concerns. Cytoscape Web extends the Cytoscape ecosystem with collaborative workflows for network visualization, though focusing on core functionalities with basic filtering and styling \citep{onoCytoscapeWebBringing2025}. Gephi Lite uses sigma.js for efficient WebGL-based rendering, though it compromises customization, filtering, and advanced styling. Similarly, Graphia provides both a desktop and web client with a powerful 2D/3D rendering engine, but focuses less on user-friendly styling, highlighting, grouping and filtering, prioritizing efficiency, enrichment and layout. - -We have developed Graph Lens Lite (GLL) to address the need for a browser-based tool that combines visualization power with an intuitive, streamlined interface, enabling researchers to rapidly explore and evaluate complex networks without requiring expertise in specialized software. GLL offers an expressive query language alongside graphical user interface (GUI)-based filtering, visual grouping, and fine-grained styling controls. Users can compute network metrics, apply customizable layouts, and map data attributes to visual scales. GLL exports networks as portable JSON files that capture the full application state for seamless collaboration, or as data tables for downstream analysis. - -\section{Materials and methods} - -\begin{figure*}[!t] - \centering - \includegraphics[width=\textwidth,height=\textheight,keepaspectratio]{main-figure.png} - \caption{The graphical user interface of Graph Lens Lite. (1) Main network view, (2) File import, (3) Workspace management, (4) Data editor, (5) Query editor, (6) Network and image export, (7) Node and edge filtering, (8) Node and edge tooltip, (9) Network metrics, (10) Selection panel, (11) Style panel, (12) Edit mode.} - \label{fig:main-figure} -\end{figure*} - -\subsection{Installation} -GLL is a free, open-source web application for visualization and exploration of networks. It is written using HTML, CSS, and JavaScript and runs on any modern browser without requiring additional installations. GLL is distributed under the MIT license and its source code is publicly available on GitHub. - -\subsection{Data input} -GLL accepts spreadsheets containing node and edge tables, or a JSON format preserving the application state. Input data require an `ID' column for nodes and `Source ID/Target ID' columns for edges. Optional columns may specify visual attributes such as labels, shapes, sizes, colors, or coordinates. Custom properties containing user data can be added as new columns, with optional group labels in square brackets. A template with column specifications, example values, and supported data types is available on GitHub and can also be generated from within the application. Additionally, a demo loader enables direct fetching of protein-protein interaction networks from the STRING database \citep{szklarczykSTRINGDatabase20252025}, with plans to extend support to further sources. The application includes an interactive guided tour that walks users through the interface and its core functionalities. - -\subsection{The graphical user interface} -The main GUI elements are shown in Figure 1. - -\noindent -\textbf{(1) Main network view:} The central window for displaying and interactively exploring the loaded network. \\ -\textbf{(2) File import:} Load files in spreadsheet or GLL JSON format. \\ -\textbf{(3) Workspace management:} Save and switch workspaces, reset layout, fit graph to view, and hide disconnected nodes. \\ -\textbf{(4) Data editor:} Interactive table for modifying (adding, deleting, editing) nodes and edges and their properties as well as exporting currently filtered nodes and edges. \\ -\textbf{(5) Query editor:} Custom query language for nested graph filtering supporting Boolean (AND/OR/NOT), comparison (>=,<=,..), and set-membership (IN [..]) operators. \\ -\textbf{(6) Network and image export:} Save the application state to a portable GLL JSON file or export the current view as PNG. \\ -\textbf{(7) Node and edge filtering:} GUI-based filtering and visual grouping, synchronized with the query language. \\ -\textbf{(8) Node and edge tooltip:} Displays metadata for the selected node or edge elements on click. \\ -\textbf{(9) Network metrics:} Controls for computing and applying network metrics to filter and select graph elements. \\ -\textbf{(10) Selection panel:} Shows current selection status, lasso selection, and navigation history. An expandable section provides additional focus, search, and layout controls. \\ -\textbf{(11) Style panel:} Edit node and edge styles, map parameters and network metrics to visual scales (continuous and discrete color scales, numeric size scales), and customize bubble sets. \\ -\textbf{(12) Edit mode:} Fine-grained controls for GUI-based filtering. \\ - -\subsection{Usage} -\subsubsection{Loading data} -GLL starts by loading network data from spreadsheet files containing node and edge tables (Figure 1.2) or from JSON files. User-supplied properties (coordinates, styling options) are applied where specified. Users can further decide to fetch demo data from the STRING database. The workspace management panel (Figure 1.3) allows switching between independent environments, each preserving its own aesthetics, layouts, filters, and queries. - -\subsubsection{Exploring the network} -Once loaded, users can explore the network structure through multiple approaches. The selection panel (Figure 1.10) enables element search, selection, and expansion to connected neighbors. Metadata for selected node and edge elements appear in specific tooltips (Figure 1.8). For deeper analysis, GLL computes topological metrics (e.g., node centrality measures including degree, betweenness, or PageRank) to identify key nodes within the network (Figure 1.9). Users can select a metric and examine one or more highly ranked nodes, with graph-level statistics displayed in the metric panel. In-app documentation describes each metric's methodology and provides references to relevant literature. - -\subsubsection{Filtering the network} -GLL supports network filtering through graphical controls (Figure 1.7), with an edit mode for precise input (Figure 1.12), or a query editor for complex operations (Figure 1.5). Node and edge properties can be used for filtering and selecting through either interface. The query editor uses a left-to-right syntax and ssupports Boolean operators (AND, OR, NOT), comparison (>=,<=), and set membership (IN) operators, as well as nested conditions using parentheses. Contextual help is accessible via an adjacent icon. - -\subsubsection{Styling the network} -The styling panel (Figure 1.11) offers extensive visual customization options for nodes, edges, and groups. Properties such as geometry (shape, size), color (fill, border, line), labels (text, placement, font), and annotations (badges, halos, arrows) can be configured independently for each element type. Up to four bubble sets allow visual grouping of nodes with adjustable appearance. Numeric and categorical attributes, whether user-supplied or computed from network metrics, can be dynamically mapped to visual features like color and size to highlight importance. - -\subsubsection{Editing network data} -GLL includes an integrated data editor (Figure 1.4) for modifying network data in tabular format. Supported operations include column sorting, cell editing, node and edge addition or deletion, table reset, and export to spreadsheet format. Users can also add new columns to define custom properties for nodes or edges, which become immediately available in the filtering and styling interfaces, enabling graph creation and manipulation entirely within the application. - -\subsubsection{Exporting results} -A graph can be exported in GLL's native JSON format or as a high-resolution image (Figure 1.6). JSON exports preserve the complete application state, including views, filters, coordinates, and styling, enabling session restoration and collaborative sharing. - -\section{Application use case} -\subsection{Visualizing a network-based model of autosomal dominant polycystic kidney disease (ADPKD)} -Autosomal Dominant Polycystic Kidney Disease (ADPKD) is the most common inherited kidney disorder, characterized by the gradual development and enlargement of multiple cysts in both kidneys, which ultimately leads to a progressive deterioration in kidney function \citep{formicaMolecularPathwaysInvolved2020}. ADPKD is a multi-factorial disease involving different disease mechanisms and molecular pathways. The exact mechanisms of ADPKD development and progression are still not fully understood and a better understanding of disease pathobiology is the basis for the discovery of novel drug targets and the development of new treatment options. We consolidated a set of biomedical data on ADPKD to model disease pathobiology and visualize molecular interactions using GLL. Literature associated ADPKD molecular features (genes and proteins) were extracted from Delta4's Hyper-C software platform that consists of sentence-level literature co-annotations between individual molecular features and ADPKD. This set of literature-associated ADPKD molecular features was complemented by genes showing differential expression in ADPKD as compared to healthy tissue based on Omics data. We downloaded the ADPKD bulk RNA-Seq dataset with the accession number GSE7869 from the Gene Expression Omnibus and re-analyzed the dataset to generate lists of differentially expressed genes \citep{songSystemsBiologyAutosomal2009}. Global gene expression profiling was conducted on renal cysts of different volumes, and non-cancerous renal cortical tissue from nephrectomized kidneys were used as controls. Differential gene expression analysis involved data normalization, statistical modeling using linear models, identification of regulated genes based on log2 fold-change (log2FC) and adjusted p-values. Ultimately, the ADPKD phenotype model was enriched with 20 genes which showed absolute log2FC > 2 and adjusted p-values < 0.05 across all four comparisons (large, medium, and small cysts, as well as minimally cystic tissue as compared to healthy tissue from nephrectomized kidneys respectively). In addition we re-analyzed a single-nucleus RNA sequencing (sn-RNA-Seq) dataset with the GEO accession number GSE185948, offering insights into the cellular heterogeneity of ADPKD \citep{mutoDefiningCellularComplexity2022}. Genes with a p-value < 0.05 were included as candidates in the analysis. Ultimately, the ADPKD phenotype molecular model was enriched with genes that were expressed in at least one cell type with absolute log2FC values > 2.5. In addition we retrieved information on genetic associations with ADPKD from the OpenTargets platform \citep{bunielloOpenTargetsPlatform2025}. Expression in healthy tissue and relevant renal cell types of molecular features of the ADPKD molecular model, quantified as normalized transcripts per million (nTPMs), were extracted from the Human Protein Atlas consensus expression profile dataset \citep{karlssonSingleCellType2021}. Information on subcellular location for features of the ADPKD molecular model was retrieved from UniProt \citep{ahmadUniProtWebsiteAPI2025}. Finally, we assigned molecular features of the ADPKD molecular model to mechanisms being discussed in recent reviews to be associated with ADPKD disease development and progression. Assignment of molecular features to mechanisms was done using the Gene Ontology (GO) annotation. - -The final constructed ADPKD molecular model consisted of 263 molecular features that were connected by 1574 edges (\textbf{Supplementary datafile S1}). Edges in the constructed network consisted of direct protein-protein interactions consolidated from IntAct \citep{orchardMIntActProjectIntAct2014}, BioGRID \citep{oughtredBioGRIDDatabaseComprehensive2021}, and Reactome \citep{milacicReactomePathwayKnowledgebase2024} as well as of literature sentence-level co-annotations of publications focusing on ADPKD using the catalogs of molecular features and phenotypes as given in Delta4's Hyper-C software platform. - -The constructed molecular model can now be used to explore the pathobiology of ADPKD by either filtering for (i) certain molecular mechanisms, (ii) individual cell types, (iii) the most up- or down-regulated genes in renal tissue or specific cell types, (iv) the most central genes based on graph properties like node degree or betweenness among others. Figure 1 for example shows genes of the Wnt signaling pathway (in green for positive modulation of the pathway, red for negative modulation of the pathway, dark blue for modulation of the pathway, and light blue for genes being pathway members without any further information on regulation) as well as additional molecules being associated with ADPKD that are linked to members of the Wnt signaling pathway (in grey) via either protein-protein interactions or co-annotations in the context of ADPKD. This network view allows investigating novel connections of Wnt signaling pathway members with ADPKD-associated proteins. - -The molecular model can subsequently be used to (i) screen for compounds showing beneficial impact on dysregulated disease mechanisms as shown previously in other renal diseases \citep{gebeshuberComputationalDrugRepositioning2023}, (ii) identify drug targets of interest, investigate cell-cell interaction analysis to identify relevant receptor-ligand pairs following previously reported analyses \citep{evgeniouMetaAnalysisHumanTranscriptomics2021}, or (iii) prioritize drug targets for the development of new chemical entities. - -\section{Conclusion} -We have developed Graph Lens Lite for visualizing, exploring and sharing biological networks. GLL is designed for portability with a focus on extensive visual customization. The entire application, together with an average sized graph containing approximately 1000 nodes, can be packed into an archive of only a few megabytes. Unlike comparable tools that may require installation, GLL requires only a web browser for core functionality. GLL is distributed as platform-specific Electron bundles for Windows, macOS, and Linux, as well as a platform-independent, self-contained HTML file. This minimal distribution enables users to load demonstration data or create and work with templates without external dependencies or API calls, ensuring long-term stability and offline operation. - -\section*{Acknowledgments} -The authors want to thank all working group members of the EU COST Action Program PerMediK (Personalized Medicine in Chronic Kidney Disease: Improved outcome based on Big Data) for fruitful discussions during the regular COST Action project meetings. - -\section*{Author contributions} -M.L.: conceptualization; resources; software; visualization; writing - original draft, review \& editing. K.K.-I.: data curation; validation; writing - review \& editing. L.F.: validation; writing - review \& editing. S.W.: validation; writing - review \& editing. F.B.: validation; writing - review \& editing. E.B.: validation; writing - review \& editing. L.G.: validation; writing - review \& editing. P.A.: validation; writing - review \& editing. P.H.: data curation; validation; writing - review \& editing. J.L.: data curation; funding acquisition; validation; writing - review \& editing. P.P.: conceptualization; data curation; funding acquisition; supervision; validation; writing - original draft, review \& editing. K.K.: funding acquisition; supervision; validation; writing - review \& editing. - -\section*{Supplementary data} -Supplementary datafile S1: The ADPKD molecular model input file - -\section*{Conflict of interest} -K.K. is a co-founder of Delta4 GmbH (Vienna, Austria). M.L., K.K.-I., L.F., S.W., F.B., E.B., L.G., P.A., and P.P. are employed at Delta4 GmbH (Vienna, Austria). - -\section*{Funding} -This project has received funding from the Austrian Research Promotion Agency (FFG) under grant agreement number 915133 (ADPKD Drug Discovery). Enrico Bono is supported by a grant from the European Union's Horizon Europe Marie Skłodowska-Curie Actions Doctoral Networks program project PICKED (HORIZON–\allowbreak{}MSCA–\allowbreak{}2023–\allowbreak{}DN-01, grant number 101168626). Louiza Galou is supported by a grant from the European Union's Horizon Europe Marie Skłodowska-Curie Actions Doctoral Networks Industrial Doctorates program project PROMOTE (HORIZON–MSCA–2023– DN-01, grant number 101169245). Paul Perco and Matthias Ley are members of and would like to cordially thank the EU COST Action Program PerMediK, CA21165, supported by COST (European Cooperation in Science and Technology). - -\section*{Data availability} -The source code, test data, templates and documentation are available at GitHub (\url{https://github.com/Delta4AI/GraphLensLite}). - diff --git a/package.json b/package.json index aa7e02e..ebe0052 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graph-lens-lite", - "version": "1.16.0", + "version": "1.16.1", "main": "src/package/electron_app.js", "description": "Visualise and explore property graphs in a lightweight desktop app.", "homepage": "https://github.com/Delta4AI/GraphLensLite", diff --git a/scripts/add_reference_to_manuscript.py b/scripts/add_reference_to_manuscript.py deleted file mode 100755 index 9e7a397..0000000 --- a/scripts/add_reference_to_manuscript.py +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env python3 -"""Fetch a PubMed citation by PMID and append it to shared/reference.bib.""" - -import json -import re -import sys -import urllib.request -from pathlib import Path - -ESUMMARY_URL = ( - "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi" - "?db=pubmed&retmode=json&id={pmid}" -) - -REPO_ROOT = Path(__file__).resolve().parent.parent -BIB_FILE = REPO_ROOT / "manuscript" / "shared" / "reference.bib" - - -# ── helpers ────────────────────────────────────────────────────────────────── - -def die(msg: str) -> None: - print(f"Error: {msg}", file=sys.stderr) - sys.exit(1) - - -def fetch_summary(pmid: str) -> dict: - """Return the esummary record for a single PMID.""" - url = ESUMMARY_URL.format(pmid=pmid) - try: - with urllib.request.urlopen(url, timeout=15) as resp: - data = json.loads(resp.read()) - except Exception as e: - die(f"Failed to fetch PMID {pmid}: {e}") - - results = data.get("result", {}) - if pmid not in results: - if "error" in results.get(pmid, {}): - die(f"NCBI error: {results[pmid]['error']}") - die(f"PMID {pmid} not found.") - return results[pmid] - - -def make_citekey(rec: dict, pmid: str) -> str: - """Build a cite key like 'ota2015positive' from the record.""" - first_author = rec.get("sortfirstauthor", "unknown") - surname = first_author.split()[0].lower() - # strip non-alpha - surname = re.sub(r"[^a-z]", "", surname) - - year = re.match(r"\d{4}", rec.get("pubdate", "")) - year = year.group(0) if year else "nodate" - - title_word = "" - title = rec.get("title", "") - for word in title.split(): - cleaned = re.sub(r"[^a-z]", "", word.lower()) - if len(cleaned) > 3: - title_word = cleaned - break - - return f"{surname}_{title_word}_{year}" if title_word else f"{surname}_{year}_{pmid}" - - -def format_authors(authors: list[dict]) -> str: - """Convert [{'name': 'Ota T', ...}, ...] to 'Ota, T. and Maeda, M.'.""" - names = [] - for a in authors: - if a.get("authtype") != "Author": - continue - parts = a["name"].rsplit(" ", 1) - if len(parts) == 2: - names.append(f"{parts[0]}, {parts[1]}.") - else: - names.append(parts[0]) - return " and ".join(names) - - -def extract_doi(rec: dict) -> str: - eloc = rec.get("elocationid", "") - m = re.search(r"10\.\S+", eloc) - return m.group(0) if m else "" - - -def record_to_bibtex(rec: dict, pmid: str) -> str: - """Convert an esummary record to a BibTeX @article entry.""" - key = make_citekey(rec, pmid) - authors = format_authors(rec.get("authors", [])) - title = rec.get("title", "").rstrip(".") - journal = rec.get("fulljournalname", rec.get("source", "")) - year_match = re.match(r"\d{4}", rec.get("pubdate", "")) - year = year_match.group(0) if year_match else "" - volume = rec.get("volume", "") - number = rec.get("issue", "") - pages = rec.get("pages", "") - doi = extract_doi(rec) - - fields = [] - fields.append(f" title={{{title}}}") - fields.append(f" author={{{authors}}}") - if journal: - fields.append(f" journal={{{journal}}}") - if year: - fields.append(f" year={{{year}}}") - if volume: - fields.append(f" volume={{{volume}}}") - if number: - fields.append(f" number={{{number}}}") - if pages: - fields.append(f" pages={{{pages}}}") - if doi: - fields.append(f" doi={{{doi}}}") - fields.append(f" pmid={{{pmid}}}") - - body = ",\n".join(fields) - return f"@article{{{key},\n{body}\n}}" - - -def key_exists(key: str) -> bool: - """Check whether a cite key already exists in the bib file.""" - if not BIB_FILE.exists(): - return False - content = BIB_FILE.read_text() - return re.search(rf"@\w+\{{\s*{re.escape(key)}\s*,", content) is not None - - -def pmid_exists(pmid: str) -> str | None: - """Return the cite key of an existing entry with this PMID, or None.""" - if not BIB_FILE.exists(): - return None - content = BIB_FILE.read_text() - # Match pmid = {XXXX} allowing whitespace variations - pattern = rf"pmid\s*=\s*\{{{re.escape(pmid)}\}}" - match = re.search(pattern, content) - if not match: - return None - # Walk backwards from the match to find the cite key - before = content[:match.start()] - key_match = re.findall(r"@\w+\{([^,]+),", before) - return key_match[-1].strip() if key_match else "unknown" - - -# ── main ───────────────────────────────────────────────────────────────────── - -def main() -> None: - if len(sys.argv) != 2 or not sys.argv[1].strip().isdigit(): - print(f"Usage: {sys.argv[0]} ") - sys.exit(1) - - pmid = sys.argv[1].strip() - print(f"Fetching PMID {pmid} …") - - rec = fetch_summary(pmid) - bib = record_to_bibtex(rec, pmid) - - # Duplicate checks - existing = pmid_exists(pmid) - if existing: - die(f"PMID {pmid} already exists in {BIB_FILE.name} as '{existing}'.") - - cite_key = bib.split("{", 1)[1].split(",", 1)[0] - if key_exists(cite_key): - die(f"Cite key '{cite_key}' already exists in {BIB_FILE.name}.") - - print() - print(bib) - print() - - answer = input("Add this entry to manuscript/shared/reference.bib? [Y/n] ").strip().lower() - if answer in ("", "y", "yes"): - with open(BIB_FILE, "a") as f: - f.write("\n" + bib + "\n") - print(f"✓ Appended as '{cite_key}' to {BIB_FILE}") - else: - print("Aborted.") - - -if __name__ == "__main__": - main() diff --git a/scripts/package_latex_submission.py b/scripts/package_latex_submission.py deleted file mode 100755 index f8d9bdc..0000000 --- a/scripts/package_latex_submission.py +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env python3 -"""Package a flat, submission-ready zip from the multi-venue LaTeX setup.""" - -import re -import shutil -import subprocess -import sys -import tempfile -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parent.parent -MANUSCRIPT = REPO_ROOT / "manuscript" -SHARED = MANUSCRIPT / "shared" - -VENUES = { - "oxford": { - "support_globs": ["*.cls", "*.bst"], - }, - "biorxiv": { - "support_globs": ["*.cls", "*.bst", "*.sty"], - }, -} - -# Paths in main.tex that reference the shared directory -SHARED_PATH_PATTERNS = [ - (re.compile(r"\{\.\.\/shared\/Fig\/\}"), "{./}"), - (re.compile(r"\{\.\.\/shared\/([^}]+)\}"), r"{\1}"), - (re.compile(r"\\input\{\.\.\/shared\/([^}]+)\}"), r"\\input{\1}"), -] - - -# ── helpers ────────────────────────────────────────────────────────────────── - -def die(msg: str) -> None: - print(f"Error: {msg}", file=sys.stderr) - sys.exit(1) - - -def pick_venue() -> str: - """Let the user choose a venue or accept a CLI argument.""" - available = [v for v in VENUES if (MANUSCRIPT / v).is_dir()] - if not available: - die("No venue directories found in manuscript/.") - - if len(sys.argv) > 1: - venue = sys.argv[1].strip().lower() - if venue not in available: - die(f"Unknown venue '{venue}'. Available: {', '.join(available)}") - return venue - - if len(available) == 1: - return available[0] - - print("Available venues:") - for i, v in enumerate(available, 1): - print(f" {i}) {v}") - choice = input(f"Select venue [1-{len(available)}]: ").strip() - try: - return available[int(choice) - 1] - except (ValueError, IndexError): - die("Invalid selection.") - return "" # unreachable - - -def collect_files(venue: str) -> list[tuple[Path, str]]: - """Return (source_path, flat_name) pairs for all files to include.""" - venue_dir = MANUSCRIPT / venue - files: list[tuple[Path, str]] = [] - - # main.tex and pre-compiled bibliography - files.append((venue_dir / "main.tex", "main.tex")) - bbl = venue_dir / "main.bbl" - if bbl.exists(): - files.append((bbl, "main.bbl")) - - # venue-specific support files (cls, bst, sty) - for glob in VENUES[venue]["support_globs"]: - for p in sorted(venue_dir.glob(glob)): - files.append((p, p.name)) - - # shared content and bibliography - for name in ["content.tex", "abbreviations.tex", "reference.bib"]: - p = SHARED / name - if p.exists(): - files.append((p, name)) - - # figures - fig_dir = SHARED / "Fig" - if fig_dir.is_dir(): - for p in sorted(fig_dir.iterdir()): - if p.is_file(): - files.append((p, p.name)) - - return files - - -def flatten_main_tex(path: Path) -> None: - """Rewrite main.tex so all paths point to the flat directory.""" - text = path.read_text() - for pattern, replacement in SHARED_PATH_PATTERNS: - text = pattern.sub(replacement, text) - path.write_text(text) - - -def compile_pdf(work_dir: Path) -> Path: - """Run pdflatex and return the path to the resulting PDF.""" - pdf = work_dir / "main.pdf" - cmd = ["pdflatex", "-interaction=nonstopmode", "main.tex"] - result = subprocess.run(cmd, cwd=work_dir, capture_output=True) - if not pdf.exists(): - output = result.stdout.decode("utf-8", errors="replace") - print(output[-2000:] if len(output) > 2000 else output) - die("pdflatex failed — see output above.") - return pdf - - -def open_pdf(pdf: Path) -> None: - """Open the PDF with the system viewer.""" - opener = shutil.which("xdg-open") or shutil.which("open") - if opener: - subprocess.Popen([opener, str(pdf)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - else: - print(f" Open manually: {pdf}") - - -def clean_build_artifacts(work_dir: Path) -> None: - """Remove LaTeX build artifacts, keeping only source files.""" - extensions = {".aux", ".log", ".out", ".fls", ".fdb_latexmk", ".blg", ".pdf", ".synctex.gz"} - for p in work_dir.iterdir(): - if p.suffix in extensions: - p.unlink() - - -# ── main ───────────────────────────────────────────────────────────────────── - -def main() -> None: - venue = pick_venue() - print(f"Packaging '{venue}' submission …\n") - - files = collect_files(venue) - missing = [src for src, _ in files if not src.exists()] - if missing: - die("Missing files:\n " + "\n ".join(str(p) for p in missing)) - - # 1. Create temp directory and copy files - tmp = Path(tempfile.mkdtemp(prefix="gll-submission-")) - print(f"Working in {tmp}\n") - for src, name in files: - shutil.copy2(src, tmp / name) - print(f" {name:<40s} ← {src.relative_to(MANUSCRIPT)}") - - # 2. Flatten paths in main.tex - flatten_main_tex(tmp / "main.tex") - print("\n Flattened paths in main.tex") - - # 3. Compile test PDF - print("\n Compiling test PDF …") - pdf = compile_pdf(tmp) - print(f" OK — {pdf.stat().st_size / 1024:.0f} KB\n") - - # 4. Let user verify - open_pdf(pdf) - answer = input("Verify the PDF. Package into zip? [Y/n] ").strip().lower() - if answer not in ("", "y", "yes"): - shutil.rmtree(tmp) - print("Aborted — temp directory removed.") - return - - # 5. Clean build artifacts - clean_build_artifacts(tmp) - - # 6. Create zip - zip_name = f"{venue}-latex-submission" - zip_path = MANUSCRIPT / zip_name - shutil.make_archive(str(zip_path), "zip", tmp) - final_zip = zip_path.with_suffix(".zip") - size_mb = final_zip.stat().st_size / (1024 * 1024) - - # 7. Clean up temp directory - shutil.rmtree(tmp) - - print(f"\n Created {final_zip.relative_to(REPO_ROOT)} ({size_mb:.1f} MB)") - print(" Temp directory removed.") - - -if __name__ == "__main__": - main() diff --git a/src/graph/core.js b/src/graph/core.js index 957eecd..0c1bfc2 100644 --- a/src/graph/core.js +++ b/src/graph/core.js @@ -1,12 +1,8 @@ -import { StaticUtilities } from "../utilities/static.js"; -import { replaceColorScale } from "../utilities/color_scale_picker.js"; -import { replaceNumericScale } from "../utilities/numeric_scale_picker.js"; -import { initTheme, nodeLabelColorForTheme } from "../utilities/theme.js"; -import { - buildGraphologyGraph, - makeNodeReducer, - makeEdgeReducer, -} from "./graph_model.js"; +import { StaticUtilities } from '../utilities/static.js'; +import { replaceColorScale } from '../utilities/color_scale_picker.js'; +import { replaceNumericScale } from '../utilities/numeric_scale_picker.js'; +import { initTheme, nodeLabelColorForTheme } from '../utilities/theme.js'; +import { buildGraphologyGraph, makeNodeReducer, makeEdgeReducer } from './graph_model.js'; // NOTE: sigma_adapter.js is imported lazily in createGraphInstance(), never // statically: the sigma vendor bundle probes WebGL at module scope // (@sigma/node-image reads MAX_TEXTURE_SIZE from a throwaway context), so a @@ -16,7 +12,7 @@ import { isWebGL2Available, renderWebGLUnavailableMessage, WEBGL2_ERROR_MESSAGE, -} from "./webgl_support.js"; +} from './webgl_support.js'; // One-time listener registration guards. These must live at module scope: // the Cache singleton is reset *in place* on every file load (Cache.reset()), @@ -40,19 +36,14 @@ class GraphCoreManager { for (let section in nodeOrEdge.D4Data) { for (let subsection in nodeOrEdge.D4Data[section]) { for (let prop in nodeOrEdge.D4Data[section][subsection]) { - yield [ - section, - subsection, - prop, - nodeOrEdge.D4Data[section][subsection][prop], - ]; + yield [section, subsection, prop, nodeOrEdge.D4Data[section][subsection][prop]]; } } } } async decideToRenderOrDraw(forceRender = false) { - await this.cache.ui.showLoading("Loading", "Deciding to render or draw .."); + await this.cache.ui.showLoading('Loading', 'Deciding to render or draw ..'); await new Promise((resolve) => requestAnimationFrame(resolve)); if (this.cache.EVENT_LOCKS.QUERY_SELECTION_EVENT) { @@ -74,7 +65,7 @@ class GraphCoreManager { // that flip it while mutating node.style.x/y on cache.nodeRef refs must // push their own updateNodeData payload. See layoutSelectedNodes. if (this.cache.styleChanged) { - await this.cache.ui.showLoading("Loading", "Updating graph .."); + await this.cache.ui.showLoading('Loading', 'Updating graph ..'); await new Promise((resolve) => requestAnimationFrame(resolve)); await this.cache.graph.updateData({ nodes: [...this.cache.nodeRef.values()], @@ -83,11 +74,11 @@ class GraphCoreManager { this.cache.styleChanged = false; this.cache.labelStyleChanged = false; } - await this.cache.ui.showLoading("Loading", "Rendering graph .."); + await this.cache.ui.showLoading('Loading', 'Rendering graph ..'); await new Promise((resolve) => requestAnimationFrame(resolve)); return await this.cache.graph.render(); } else { - await this.cache.ui.showLoading("Loading", "Redrawing graph .."); + await this.cache.ui.showLoading('Loading', 'Redrawing graph ..'); await new Promise((resolve) => requestAnimationFrame(resolve)); return await this.cache.graph.draw(); } @@ -124,7 +115,7 @@ class GraphCoreManager { // and leave cache.graph null — the same state as "no data loaded", so // the rest of the app chrome keeps working; load paths already guard // on a null graph after this call. - const containerEl = document.getElementById("innerGraphContainer"); + const containerEl = document.getElementById('innerGraphContainer'); if (!isWebGL2Available()) { renderWebGLUnavailableMessage(containerEl); this.cache.ui.error(WEBGL2_ERROR_MESSAGE); @@ -140,12 +131,12 @@ class GraphCoreManager { // from elementStates so hover can never corrupt selection state. const hoverIds = new Set(); try { - const { SigmaAdapter } = await import("./sigma_adapter.js"); + const { SigmaAdapter } = await import('./sigma_adapter.js'); // initTheme (not currentTheme): re-resolves stored/OS preference so a // graph constructed before the DOMContentLoaded theme boot still gets // the right label colors. Idempotent after boot. const theme = initTheme(document, window); - this.cache.graph = new SigmaAdapter(this.cache, "innerGraphContainer", { + this.cache.graph = new SigmaAdapter(this.cache, 'innerGraphContainer', { nodeReducer: makeNodeReducer(this.cache, elementStates, hoverIds), edgeReducer: makeEdgeReducer(this.cache, elementStates, hoverIds), elementStates, @@ -185,8 +176,7 @@ class GraphCoreManager { // If layout has no positions yet but has a layoutType, apply that layout algorithm once if (layout.positions.size === 0 && layout.layoutType) { - const internals = - this.cache.DEFAULTS.LAYOUT_INTERNALS[layout.layoutType] || {}; + const internals = this.cache.DEFAULTS.LAYOUT_INTERNALS[layout.layoutType] || {}; await this.cache.graph.setLayout({ type: layout.layoutType, ...internals, @@ -203,43 +193,34 @@ class GraphCoreManager { if (this.cache.EVENT_LOCKS.ONCE_AFTER_RENDER_RUNNING) return; try { - this.cache.ui.debug("ONCE AFTER RENDER"); - await this.cache.ui.showLoading("Post-processing", "Post-processing .."); + this.cache.ui.debug('ONCE AFTER RENDER'); + await this.cache.ui.showLoading('Post-processing', 'Post-processing ..'); await new Promise((resolve) => requestAnimationFrame(resolve)); this.cache.EVENT_LOCKS.ONCE_AFTER_RENDER_RUNNING = true; - await this.cache.ui.showLoading( - "Post-processing", - "Registering event listeners ..", - ); + await this.cache.ui.showLoading('Post-processing', 'Registering event listeners ..'); await new Promise((resolve) => requestAnimationFrame(resolve)); this.registerHotkeyEvents(); this.registerGlobalEventListeners(); await this.registerPluginStates(); // to initially fill caches related to the query/filters, preRenderEvent is called without rendering afterwards - await this.cache.ui.showLoading("Post-processing", "Pre-render event .."); + await this.cache.ui.showLoading('Post-processing', 'Pre-render event ..'); await new Promise((resolve) => requestAnimationFrame(resolve)); await this.preRenderEvent(); - await this.cache.ui.showLoading( - "Post-processing", - "Updating metrics UI ..", - ); + await this.cache.ui.showLoading('Post-processing', 'Updating metrics UI ..'); await new Promise((resolve) => requestAnimationFrame(resolve)); await this.cache.metrics.updateMetricUI(); - await this.cache.ui.showLoading( - "Post-processing", - "Finalizing rendering ..", - ); + await this.cache.ui.showLoading('Post-processing', 'Finalizing rendering ..'); await new Promise((resolve) => requestAnimationFrame(resolve)); if (this.cache.EVENT_LOCKS.TRIGGER_SET_LAYOUT_ONCE) { // suppresses the info in case of loading from a json model if (this.cache.nodePositionsFromExcelImport.size !== 0) { this.cache.ui.info( - `Created view "${this.cache.DEFAULTS.CUSTOM_LAYOUT_NAME}". Applying ${this.cache.DEFAULTS.LAYOUT} layout to nodes without coordinates ..`, + `Created view "${this.cache.DEFAULTS.CUSTOM_LAYOUT_NAME}". Applying ${this.cache.DEFAULTS.LAYOUT} layout to nodes without coordinates ..` ); } await this.cache.graph.setLayout({ @@ -252,7 +233,7 @@ class GraphCoreManager { await this.cache.graph.render(); if (this.cache.EVENT_LOCKS.TRIGGER_SET_LAYOUT_ONCE) { - this.cache.ui.debug("Initially persisting custom layout .."); + this.cache.ui.debug('Initially persisting custom layout ..'); await this.cache.lm.persistNodePositions(); this.cache.EVENT_LOCKS.TRIGGER_SET_LAYOUT_ONCE = false; } @@ -260,7 +241,7 @@ class GraphCoreManager { await this.applyHideDisconnectedState(); } catch (errorMsg) { this.cache.ui.error(`Error in initial AFTER_RENDER: ${errorMsg}`); - this.cache.ui.error("Graph setup failed. Please check your input data."); + this.cache.ui.error('Graph setup failed. Please check your input data.'); await this.cache.ui.hideLoading(); } finally { this.cache.EVENT_LOCKS.ONCE_AFTER_RENDER_RUNNING = false; @@ -268,24 +249,21 @@ class GraphCoreManager { } async toggleCleanUpDanglingElements(btn) { - const shouldEnable = btn.classList.contains("red"); - const currentLayout = - this.cache.data.layouts[this.cache.data.selectedLayout]; + const shouldEnable = btn.classList.contains('red'); + const currentLayout = this.cache.data.layouts[this.cache.data.selectedLayout]; if (shouldEnable) { - btn.classList.remove("red"); - btn.classList.add("green", "highlight"); - btn.title = - "Show all nodes and edges, irrespectively of their connectedness."; - btn.textContent = "👁"; + btn.classList.remove('red'); + btn.classList.add('green', 'highlight'); + btn.title = 'Show all nodes and edges, irrespectively of their connectedness.'; + btn.textContent = '👁'; currentLayout.hideDisconnectedNodes = true; await this.hideDanglingElements(); } else { - btn.classList.remove("green", "highlight"); - btn.classList.add("red"); - btn.title = - "Hide all nodes and edges that are not connected to any other node or edge."; - btn.textContent = "🚫"; + btn.classList.remove('green', 'highlight'); + btn.classList.add('red'); + btn.title = 'Hide all nodes and edges that are not connected to any other node or edge.'; + btn.textContent = '🚫'; currentLayout.hideDisconnectedNodes = false; await this.showDanglingElements(); } @@ -316,17 +294,18 @@ class GraphCoreManager { return true; } - async hideDanglingElements() { + // Pure computation: populate hiddenDanglingNodeIDs/hiddenDanglingEdgeIDs by + // iterating the CURRENT show sets until a fixpoint. Hiding a dangling node + // can strand its neighbour, so we repeat until no further changes. No side + // effects beyond the cache sets — callers own bubble/render updates. + computeDanglingElements() { let changes; do { changes = false; for (let nodeID of this.cache.nodeIDsToBeShown) { - if ( - !this.nodeHasAVisibleEdge(nodeID) && - !this.cache.hiddenDanglingNodeIDs.has(nodeID) - ) { + if (!this.nodeHasAVisibleEdge(nodeID) && !this.cache.hiddenDanglingNodeIDs.has(nodeID)) { this.cache.hiddenDanglingNodeIDs.add(nodeID); changes = true; } @@ -342,15 +321,19 @@ class GraphCoreManager { } } } while (changes); + } + + async hideDanglingElements() { + this.computeDanglingElements(); // Update bubble groups to exclude hidden dangling nodes await this.cache.bs.updateBubbleSetIfChanged(); await this.cache.fm.handleFilterEvent( - "Hiding Elements", - "Hiding nodes and edges that are not connected to any other node or edge.", + 'Hiding Elements', + 'Hiding nodes and edges that are not connected to any other node or edge.', null, - false, + false ); } @@ -362,36 +345,32 @@ class GraphCoreManager { await this.cache.bs.updateBubbleSetIfChanged(); await this.cache.fm.handleFilterEvent( - "Showing Elements", - "Showing all previously hidden nodes and edges that are not connected to any other node or edge.", + 'Showing Elements', + 'Showing all previously hidden nodes and edges that are not connected to any other node or edge.', null, - false, + false ); } updateHideDisconnectedButtonState() { - const btn = document.getElementById("hideDisconnectedBtn"); + const btn = document.getElementById('hideDisconnectedBtn'); if (!btn) return; - const currentLayout = - this.cache.data.layouts[this.cache.data.selectedLayout]; + const currentLayout = this.cache.data.layouts[this.cache.data.selectedLayout]; if (currentLayout && currentLayout.hideDisconnectedNodes) { - btn.classList.remove("red"); - btn.classList.add("green", "highlight"); - btn.title = - "Show all nodes and edges, irrespectively of their connectedness."; - btn.textContent = "👁"; + btn.classList.remove('red'); + btn.classList.add('green', 'highlight'); + btn.title = 'Show all nodes and edges, irrespectively of their connectedness.'; + btn.textContent = '👁'; } else { - btn.classList.remove("green", "highlight"); - btn.classList.add("red"); - btn.title = - "Hide all nodes and edges that are not connected to any other node or edge."; - btn.textContent = "🚫"; + btn.classList.remove('green', 'highlight'); + btn.classList.add('red'); + btn.title = 'Hide all nodes and edges that are not connected to any other node or edge.'; + btn.textContent = '🚫'; } } async applyHideDisconnectedState() { - const currentLayout = - this.cache.data.layouts[this.cache.data.selectedLayout]; + const currentLayout = this.cache.data.layouts[this.cache.data.selectedLayout]; this.cache.hiddenDanglingNodeIDs.clear(); this.cache.hiddenDanglingEdgeIDs.clear(); this.updateHideDisconnectedButtonState(); @@ -422,15 +401,15 @@ class GraphCoreManager { await this.cache.graph.focusElement([...elementIDs]); const targetMap = isNode ? this.cache.nodeRef : this.cache.edgeRef; - await this.cache.sm.selectElements(elementIDs, targetMap, "highlight"); + await this.cache.sm.selectElements(elementIDs, targetMap, 'highlight'); setTimeout(async () => { - await this.cache.sm.selectElements([], targetMap, "highlight"); + await this.cache.sm.selectElements([], targetMap, 'highlight'); }, 2500); } async fitViewToVisibleNodes() { const visibleNodeIDs = [...this.cache.nodeIDsToBeShown].filter( - (id) => !this.cache.hiddenDanglingNodeIDs.has(id), + (id) => !this.cache.hiddenDanglingNodeIDs.has(id) ); await this.fitViewToNodes(visibleNodeIDs); } @@ -470,23 +449,20 @@ class GraphCoreManager { async updateEdges(overrides = {}, commands = []) { let colorMap = null; - if (commands.includes("set_continuous_color_scale")) { - colorMap = await this.cache.picker.pickColors("edges"); + if (commands.includes('set_continuous_color_scale')) { + colorMap = await this.cache.picker.pickColors('edges'); if (!colorMap) { - this.cache.ui.info("Aborted color picker"); + this.cache.ui.info('Aborted color picker'); return; } } let numericScaleMap = null; - if (commands.includes("set_numeric_scale")) { + if (commands.includes('set_numeric_scale')) { const propertyName = this.cache.numericPicker.currentProperty || null; - numericScaleMap = await this.cache.numericPicker.pickNumericScale( - "edges", - propertyName, - ); + numericScaleMap = await this.cache.numericPicker.pickNumericScale('edges', propertyName); if (!numericScaleMap) { - this.cache.ui.info("Aborted numeric scale picker"); + this.cache.ui.info('Aborted numeric scale picker'); return; } } @@ -495,11 +471,11 @@ class GraphCoreManager { const edge = this.cache.edgeRef.get(edgeID); for (const command of commands) { - if (command === "label_set_to_id") { + if (command === 'label_set_to_id') { edge.style.label = true; edge.style.labelText = edge.id; } - if (command === "label_set_to_label") { + if (command === 'label_set_to_label') { edge.style.label = true; edge.style.labelText = edge.label; } @@ -517,48 +493,41 @@ class GraphCoreManager { this.cache.edgeRef.set(edgeID, edge); // Save to current layout's style map (including type) - const currentLayout = - this.cache.data.layouts[this.cache.data.selectedLayout]; + const currentLayout = this.cache.data.layouts[this.cache.data.selectedLayout]; currentLayout.edgeStyles.set(edgeID, { type: edge.type, style: structuredClone(edge.style), }); } - await this.cache.style.handleStyleChangeLoadingEvent( - "Style", - "Updating Edge Styles", - ); + await this.cache.style.handleStyleChangeLoadingEvent('Style', 'Updating Edge Styles'); } async updateNodes(overrides = {}, commands = []) { let colorMap = null; - if (commands.includes("set_continuous_color_scale")) { - colorMap = await this.cache.picker.pickColors("nodes"); + if (commands.includes('set_continuous_color_scale')) { + colorMap = await this.cache.picker.pickColors('nodes'); if (!colorMap) { - this.cache.ui.info("Aborted color picker"); + this.cache.ui.info('Aborted color picker'); return; } } let numericScaleMap = null; - if (commands.includes("set_numeric_scale")) { + if (commands.includes('set_numeric_scale')) { const propertyName = this.cache.numericPicker.currentProperty || null; - numericScaleMap = await this.cache.numericPicker.pickNumericScale( - "nodes", - propertyName, - ); + numericScaleMap = await this.cache.numericPicker.pickNumericScale('nodes', propertyName); if (!numericScaleMap) { - this.cache.ui.info("Aborted numeric scale picker"); + this.cache.ui.info('Aborted numeric scale picker'); return; } } let pieResult = null; - if (commands.includes("set_pie_chart")) { + if (commands.includes('set_pie_chart')) { pieResult = await this.cache.piePicker.pickPie(); if (!pieResult) { - this.cache.ui.info("Aborted pie chart picker"); + this.cache.ui.info('Aborted pie chart picker'); return; } } @@ -566,7 +535,7 @@ class GraphCoreManager { const badgesToAdd = overrides.style?.badges; const badgePaletteToAdd = overrides.style?.badgePalette; - if (commands.includes("badge_add")) { + if (commands.includes('badge_add')) { delete overrides.style?.badges; delete overrides.style?.badgePalette; } @@ -575,12 +544,12 @@ class GraphCoreManager { const node = this.cache.nodeRef.get(nodeID); for (const command of commands) { - if (command === "badge_clear") { + if (command === 'badge_clear') { node.style.badge = false; node.style.badges = []; node.style.badgePalette = []; } - if (command === "badge_add") { + if (command === 'badge_add') { node.style.badge = true; node.style.badges = node.style.badges || []; node.style.badgePalette = node.style.badgePalette || []; @@ -595,22 +564,20 @@ class GraphCoreManager { if (badgePaletteToAdd) { node.style.badgePalette = [ ...node.style.badgePalette, - ...(Array.isArray(badgePaletteToAdd) - ? badgePaletteToAdd - : [badgePaletteToAdd]), + ...(Array.isArray(badgePaletteToAdd) ? badgePaletteToAdd : [badgePaletteToAdd]), ]; } } - if (command === "label_set_to_id") { + if (command === 'label_set_to_id') { node.style.label = true; node.style.labelText = node.id; } - if (command === "label_set_to_label") { + if (command === 'label_set_to_label') { node.style.label = true; node.style.labelText = node.label; } - if (command === "clear_pie_chart") { + if (command === 'clear_pie_chart') { delete node.style.pieSlices; delete node.style.pieMode; delete node.style.pieProperties; @@ -638,18 +605,14 @@ class GraphCoreManager { this.cache.nodeRef.set(nodeID, node); // Save to current layout's style map (including type) - const currentLayout = - this.cache.data.layouts[this.cache.data.selectedLayout]; + const currentLayout = this.cache.data.layouts[this.cache.data.selectedLayout]; currentLayout.nodeStyles.set(nodeID, { type: node.type, style: structuredClone(node.style), }); } - await this.cache.style.handleStyleChangeLoadingEvent( - "Style", - `Updating Node Styles`, - ); + await this.cache.style.handleStyleChangeLoadingEvent('Style', `Updating Node Styles`); } getTargetNodes(propID) { @@ -658,7 +621,7 @@ class GraphCoreManager { return []; } return [...this.cache.propToNodeIDs.get(propID)].filter((nodeID) => - this.cache.nodeIDsToBeShown.has(nodeID), + this.cache.nodeIDsToBeShown.has(nodeID) ); } @@ -668,7 +631,7 @@ class GraphCoreManager { return []; } return [...this.cache.propToEdgeIDs.get(propID)].filter((edgeID) => - this.cache.edgeIDsToBeShown.has(edgeID), + this.cache.edgeIDsToBeShown.has(edgeID) ); } @@ -739,17 +702,29 @@ class GraphCoreManager { } } + // Re-evaluate disconnected elements against the freshly filtered view. + // The dangling sets are recomputed from scratch every pass: a node hidden + // as dangling under the previous filter state may now be connected (and + // vice versa). Without this, a stale dangling node still passing the filter + // lands in BOTH idsToShow and idsToHide, and updateElementVisibility's + // show-then-hide diff resurfaces it on every filter change. + this.cache.hiddenDanglingNodeIDs.clear(); + this.cache.hiddenDanglingEdgeIDs.clear(); + const currentLayout = this.cache.data.layouts[this.cache.data.selectedLayout]; + if (currentLayout && currentLayout.hideDisconnectedNodes) { + this.computeDanglingElements(); + } + const nodeIDsToBeHidden = [...this.cache.nodeRef.keys()].filter( - (nodeID) => !this.cache.nodeIDsToBeShown.has(nodeID), + (nodeID) => !this.cache.nodeIDsToBeShown.has(nodeID) ); const edgeIDsToBeHidden = [...this.cache.edgeRef.keys()].filter( - (edgeID) => !this.cache.edgeIDsToBeShown.has(edgeID), + (edgeID) => !this.cache.edgeIDsToBeShown.has(edgeID) ); - const idsToShow = [ - ...this.cache.nodeIDsToBeShown, - ...this.cache.edgeIDsToBeShown, - ]; + const idsToShow = [...this.cache.nodeIDsToBeShown, ...this.cache.edgeIDsToBeShown].filter( + (id) => !this.cache.hiddenDanglingNodeIDs.has(id) && !this.cache.hiddenDanglingEdgeIDs.has(id) + ); const idsToHide = [ ...nodeIDsToBeHidden, @@ -784,15 +759,15 @@ class GraphCoreManager { // isPositionsDirty = false; // syncPositionsDebounced.cancel?.(); - const status = document.getElementById("sidebarStatusContainer"); - status.innerHTML = ""; - status.style.height = "0"; + const status = document.getElementById('sidebarStatusContainer'); + status.innerHTML = ''; + status.style.height = '0'; } registerHotkeyEvents() { if (hotkeysRegistered) return; - document.addEventListener("keydown", async (event) => { + document.addEventListener('keydown', async (event) => { // Suppress hotkeys while a workspace/layout/render is loading. The overlay // blocks pointer input but not keydown, so without this the user could // fire export/toggle/fit actions against a graph that is still settling. @@ -802,43 +777,43 @@ class GraphCoreManager { // Skip hotkeys if currently focused on an input, textarea, or select element if ( - activeElement.tagName === "INPUT" || - activeElement.tagName === "TEXTAREA" || - activeElement.tagName === "SELECT" || + activeElement.tagName === 'INPUT' || + activeElement.tagName === 'TEXTAREA' || + activeElement.tagName === 'SELECT' || activeElement.isContentEditable ) { return; } switch (event.key) { - case "p": + case 'p': await this.cache.io.exportPNG(); break; - case "s": + case 's': await this.cache.io.exportGraphAsJSON(); break; - case "f": + case 'f': await this.fitViewToVisibleNodes(); break; - case "d": + case 'd': await this.cache.ui.toggleDataEditor(); break; - case "q": + case 'q': this.cache.ui.toggleQueryEditor(); break; - case "m": + case 'm': this.cache.metrics.toggleUI(); break; - case "y": + case 'y': this.cache.ui.toggleStylingPanel(); break; - case "l": + case 'l': await this.cache.ui.toggleLassoSelection(); break; - case "h": - await this.cache.ui.toggleHoverEffect(document.getElementById("hoverToggleBtn")); + case 'h': + await this.cache.ui.toggleHoverEffect(document.getElementById('hoverToggleBtn')); break; - case "a": + case 'a': this.cache.assistant.togglePanel(); break; default: @@ -854,27 +829,13 @@ class GraphCoreManager { // are static DOM that survives file loads — same stacking hazard as hotkeys. if (globalEventsRegistered) return; - [ - "input", - "keydown", - "keyup", - "mousedown", - "mouseup", - "focus", - "blur", - "scroll", - ].forEach((evt) => - this.cache.query.text.addEventListener(evt, () => - this.cache.qm.moveCaret(), - ), + ['input', 'keydown', 'keyup', 'mousedown', 'mouseup', 'focus', 'blur', 'scroll'].forEach( + (evt) => this.cache.query.text.addEventListener(evt, () => this.cache.qm.moveCaret()) ); - document.addEventListener("selectionchange", () => { + document.addEventListener('selectionchange', () => { const sel = window.getSelection(); - if ( - sel.rangeCount && - this.cache.query.text.contains(sel.getRangeAt(0).startContainer) - ) { + if (sel.rangeCount && this.cache.query.text.contains(sel.getRangeAt(0).startContainer)) { this.cache.qm.moveCaret(); } }); @@ -887,44 +848,44 @@ class GraphCoreManager { registerTooltipExpandToggle() { window.toggleTooltipExpand = function (button) { - const tooltip = button.closest(".tooltip"); + const tooltip = button.closest('.tooltip'); if (!tooltip) return; - const isExpanded = tooltip.classList.contains("expanded"); + const isExpanded = tooltip.classList.contains('expanded'); if (isExpanded) { - tooltip.classList.remove("expanded"); - button.textContent = "⛶"; - button.title = "Expand to fit content"; + tooltip.classList.remove('expanded'); + button.textContent = '⛶'; + button.title = 'Expand to fit content'; } else { - tooltip.classList.add("expanded"); - button.textContent = "⤡"; - button.title = "Restore size"; + tooltip.classList.add('expanded'); + button.textContent = '⤡'; + button.title = 'Restore size'; } }; window.closeTooltip = function (button) { - const tooltip = button.closest(".tooltip"); + const tooltip = button.closest('.tooltip'); if (!tooltip) return; - tooltip.style.visibility = "hidden"; + tooltip.style.visibility = 'hidden'; }; } registerTooltipWheelHandler() { - const graphContainer = document.getElementById("innerGraphContainer"); + const graphContainer = document.getElementById('innerGraphContainer'); if (!graphContainer) return; graphContainer.addEventListener( - "wheel", + 'wheel', (event) => { const target = event.target; - const tooltip = target.closest(".tooltip"); + const tooltip = target.closest('.tooltip'); if (tooltip) { event.stopPropagation(); } }, - { passive: false, capture: true }, + { passive: false, capture: true } ); this.makeTooltipDraggable(graphContainer); @@ -941,17 +902,13 @@ class GraphCoreManager { e.stopPropagation(); }; - graphContainer.addEventListener("pointerdown", (e) => { - if ( - e.target.closest(".tooltip-expand-btn") || - e.target.closest(".tooltip-close-btn") - ) - return; + graphContainer.addEventListener('pointerdown', (e) => { + if (e.target.closest('.tooltip-expand-btn') || e.target.closest('.tooltip-close-btn')) return; - const header = e.target.closest(".tooltip-header"); + const header = e.target.closest('.tooltip-header'); if (!header) return; - const tooltip = header.closest(".tooltip"); + const tooltip = header.closest('.tooltip'); if (!tooltip) return; isDragging = true; @@ -962,11 +919,11 @@ class GraphCoreManager { offsetX = e.clientX - tooltipRect.left + parentRect.left; offsetY = e.clientY - tooltipRect.top + parentRect.top; - header.style.cursor = "grabbing"; + header.style.cursor = 'grabbing'; stopEvent(e); }); - document.addEventListener("pointermove", (e) => { + document.addEventListener('pointermove', (e) => { if (!isDragging || !currentTooltip) return; currentTooltip.style.left = `${e.clientX - offsetX}px`; @@ -974,18 +931,18 @@ class GraphCoreManager { stopEvent(e); }); - document.addEventListener("pointerup", (e) => { + document.addEventListener('pointerup', (e) => { if (!isDragging) return; if (currentTooltip) { - const header = currentTooltip.querySelector(".tooltip-header"); - if (header) header.style.cursor = "move"; + const header = currentTooltip.querySelector('.tooltip-header'); + if (header) header.style.cursor = 'move'; } isDragging = false; currentTooltip = null; stopEvent(e); - window.addEventListener("click", stopEvent, { + window.addEventListener('click', stopEvent, { capture: true, once: true, }); @@ -993,10 +950,11 @@ class GraphCoreManager { } async registerPluginStates() { - this.cache.ui.debug("Registering bubble set plugin instances .."); + this.cache.ui.debug('Registering bubble set plugin instances ..'); for (const group of this.cache.bs.traverseBubbleSets()) { - this.cache.INSTANCES.BUBBLE_GROUPS[group] = - await this.cache.graph.getPluginInstance(`bubbleSetPlugin-${group}`); + this.cache.INSTANCES.BUBBLE_GROUPS[group] = await this.cache.graph.getPluginInstance( + `bubbleSetPlugin-${group}` + ); } } } diff --git a/src/managers/ui_components.js b/src/managers/ui_components.js index 4a88205..db47159 100644 --- a/src/managers/ui_components.js +++ b/src/managers/ui_components.js @@ -1,12 +1,13 @@ -import {DEFAULTS} from "../config.js"; -import {StaticUtilities} from "../utilities/static.js"; +import { DEFAULTS } from '../config.js'; +import { StaticUtilities } from '../utilities/static.js'; class DropdownChecklist { constructor(propID, cache) { this.propID = propID; this.cache = cache; this.categories = this.cache.data.filterDefaults.get(propID).categories; - this.selectedCategories = this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID).categories; + this.selectedCategories = + this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID).categories; this.isVisible = false; this.sortCategories(); this.cache.propIDToDropdownChecklists.set(propID, this); @@ -43,31 +44,31 @@ class DropdownChecklist { } appendTo(parent) { - this.container = document.createElement("div"); - this.container.id = this.propID + "-dropdown"; - this.container.className = "dropdown-check-list"; + this.container = document.createElement('div'); + this.container.id = this.propID + '-dropdown'; + this.container.className = 'dropdown-check-list'; this.container.tabIndex = 100; // Create the anchor (visible clickable part) - this.anchor = document.createElement("h5"); - this.anchor.className = "anchor purple round-border"; + this.anchor = document.createElement('h5'); + this.anchor.className = 'anchor purple round-border'; this.anchor.textContent = `${this.selectedCategories.size}/${this.categories.size} selected`; - this.anchor.id = this.propID + "-dropdown-anchor"; + this.anchor.id = this.propID + '-dropdown-anchor'; this.container.appendChild(this.anchor); // Create the unordered list (dropdown items) - this.itemsList = document.createElement("ul"); - this.itemsList.className = "items"; + this.itemsList = document.createElement('ul'); + this.itemsList.className = 'items'; // add buttons on top - this.buttonContainer = document.createElement("div"); - this.buttonContainer.className = "dropdown-buttons"; + this.buttonContainer = document.createElement('div'); + this.buttonContainer.className = 'dropdown-buttons'; - this.selectAllButton = document.createElement("button"); - this.selectAllButton.textContent = "Select All"; + this.selectAllButton = document.createElement('button'); + this.selectAllButton.textContent = 'Select All'; - this.deselectAllButton = document.createElement("button"); - this.deselectAllButton.textContent = "Deselect All"; + this.deselectAllButton = document.createElement('button'); + this.deselectAllButton.textContent = 'Deselect All'; this.buttonContainer.appendChild(this.selectAllButton); this.buttonContainer.appendChild(this.deselectAllButton); @@ -75,26 +76,28 @@ class DropdownChecklist { this.itemsList.appendChild(this.buttonContainer); // Add the options as checkboxes - this.categories.forEach(option => { - const listItem = document.createElement("li"); - const checkbox = document.createElement("input"); - checkbox.type = "checkbox"; + this.categories.forEach((option) => { + const listItem = document.createElement('li'); + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; checkbox.value = option; checkbox.checked = this.selectedCategories.has(option); - checkbox.className = "hidden-checkbox"; - checkbox.addEventListener("change", async (ev) => await this.handleSelection(ev)); + checkbox.className = 'hidden-checkbox'; + checkbox.addEventListener('change', async (ev) => await this.handleSelection(ev)); - const customCheckbox = document.createElement("span"); - customCheckbox.className = "custom-checkbox"; + const customCheckbox = document.createElement('span'); + customCheckbox.className = 'custom-checkbox'; - checkbox.addEventListener("change", () => { - checkbox.checked ? customCheckbox.classList.add("checked") : customCheckbox.classList.remove("checked"); + checkbox.addEventListener('change', () => { + checkbox.checked + ? customCheckbox.classList.add('checked') + : customCheckbox.classList.remove('checked'); }); // Set initial state - if (checkbox.checked) customCheckbox.classList.add("checked"); + if (checkbox.checked) customCheckbox.classList.add('checked'); - const label = document.createElement("label"); + const label = document.createElement('label'); label.textContent = option; label.prepend(customCheckbox); label.prepend(checkbox); @@ -118,8 +121,11 @@ class DropdownChecklist { ? this.selectedCategories.add(ev.target.value) : this.selectedCategories.delete(ev.target.value); this.anchor.textContent = `${this.selectedCategories.size}/${this.categories.size} selected`; - await this.cache.fm.handleFilterEvent(ev.target.checked ? "Showing" : "Hiding" + " Elements", - `Nodes and related edges for ${this.propID} ${ev.target.value}`, this.propID); + await this.cache.fm.handleFilterEvent( + ev.target.checked ? 'Showing' : 'Hiding' + ' Elements', + `Nodes and related edges for ${this.propID} ${ev.target.value}`, + this.propID + ); // this.cache.ui.debug(`${this.propID} ${ev.target.value} ${ev.target.checked}`); } catch (err) { this.cache.ui.error(`Failed to handle category selection: ${err.message}`); @@ -129,14 +135,14 @@ class DropdownChecklist { appendListeners() { const updateDropdownPosition = () => { // Temporarily make the dropdown visible to calculate its height - this.itemsList.style.visibility = "hidden"; - this.itemsList.style.display = "block"; + this.itemsList.style.visibility = 'hidden'; + this.itemsList.style.display = 'block'; const dropdownHeight = this.itemsList.offsetHeight; - this.itemsList.style.display = ""; - this.itemsList.style.visibility = ""; + this.itemsList.style.display = ''; + this.itemsList.style.visibility = ''; - const {left, top, maxHeight} = StaticUtilities.computeDropdownPlacement({ + const { left, top, maxHeight } = StaticUtilities.computeDropdownPlacement({ anchorRect: this.anchor.getBoundingClientRect(), dropdownHeight, viewportHeight: window.innerHeight, @@ -148,34 +154,37 @@ class DropdownChecklist { // Cap height and scroll only when even the chosen side is too small. if (maxHeight != null) { this.itemsList.style.maxHeight = `${maxHeight}px`; - this.itemsList.style.overflowY = "auto"; + this.itemsList.style.overflowY = 'auto'; } else { - this.itemsList.style.maxHeight = ""; - this.itemsList.style.overflowY = ""; + this.itemsList.style.maxHeight = ''; + this.itemsList.style.overflowY = ''; } }; - this.anchor.addEventListener("click", () => { + this.anchor.addEventListener('click', () => { this.isVisible = !this.isVisible; if (this.isVisible) { updateDropdownPosition(); - document.addEventListener("scroll", updateDropdownPosition, true); - this.container.classList.add("visible"); + document.addEventListener('scroll', updateDropdownPosition, true); + this.container.classList.add('visible'); } else { - this.container.classList.remove("visible"); - document.removeEventListener("scroll", updateDropdownPosition, true); + this.container.classList.remove('visible'); + document.removeEventListener('scroll', updateDropdownPosition, true); } }); // button callbacks - this.selectAllButton.addEventListener("click", async () => await this.selectAllCategories()); - this.deselectAllButton.addEventListener("click", async () => await this.deselectAllCategories()); + this.selectAllButton.addEventListener('click', async () => await this.selectAllCategories()); + this.deselectAllButton.addEventListener( + 'click', + async () => await this.deselectAllCategories() + ); // Handle clicks outside the dropdown to close it - document.addEventListener("click", (event) => { + document.addEventListener('click', (event) => { if (!this.container.contains(event.target)) { this.isVisible = false; - this.container.classList.remove("visible"); + this.container.classList.remove('visible'); } }); } @@ -192,16 +201,20 @@ class DropdownChecklist { `input[type="checkbox"][value="${CSS.escape(category)}"]` ); checkbox.checked = true; - checkbox.nextElementSibling.classList.add("checked"); + checkbox.nextElementSibling.classList.add('checked'); this.anchor.textContent = `${this.selectedCategories.size}/${this.categories.size} selected`; } async selectAllCategories(skipFilterEvent = false) { try { - this.categories.forEach(category => this.selectedCategories.add(category)); // Add all categories + this.categories.forEach((category) => this.selectedCategories.add(category)); // Add all categories this.updateCheckboxStates(true); if (!skipFilterEvent) { - await this.cache.fm.handleFilterEvent("Showing Elements", `Nodes and related edges for ${this.propID}`, this.propID); + await this.cache.fm.handleFilterEvent( + 'Showing Elements', + `Nodes and related edges for ${this.propID}`, + this.propID + ); } } catch (err) { this.cache.ui.error(`Failed to select all categories: ${err.message}`); @@ -210,10 +223,14 @@ class DropdownChecklist { async deselectAllCategories(skipFilterEvent = false) { try { - this.categories.forEach(category => this.selectedCategories.delete(category)); // Clear all categories + this.categories.forEach((category) => this.selectedCategories.delete(category)); // Clear all categories this.updateCheckboxStates(false); if (!skipFilterEvent) { - await this.cache.fm.handleFilterEvent("Hiding Elements", `Nodes and related edges for ${this.propID}`, this.propID); + await this.cache.fm.handleFilterEvent( + 'Hiding Elements', + `Nodes and related edges for ${this.propID}`, + this.propID + ); } } catch (err) { this.cache.ui.error(`Failed to deselect all categories: ${err.message}`); @@ -221,15 +238,14 @@ class DropdownChecklist { } updateCheckboxStates(selectAll) { - Array.from(this.itemsList.querySelectorAll("input[type='checkbox']")).forEach(checkbox => { + Array.from(this.itemsList.querySelectorAll("input[type='checkbox']")).forEach((checkbox) => { checkbox.checked = selectAll; // Update checkbox state selectAll - ? checkbox.nextElementSibling.classList.add("checked") - : checkbox.nextElementSibling.classList.remove("checked"); + ? checkbox.nextElementSibling.classList.add('checked') + : checkbox.nextElementSibling.classList.remove('checked'); }); this.anchor.textContent = `${this.selectedCategories.size}/${this.categories.size} selected`; // Update anchor text } - } class InvertibleRangeSlider { @@ -240,13 +256,16 @@ class InvertibleRangeSlider { this.readCurrentFilterSettings(); this.sliderMin = defaultFilterData.lowerThreshold; this.sliderMax = defaultFilterData.upperThreshold; - const allInteger = StaticUtilities.isInteger(this.sliderMin) && StaticUtilities.isInteger(this.sliderMax) && !defaultFilterData.hasFloatValues; + const allInteger = + StaticUtilities.isInteger(this.sliderMin) && + StaticUtilities.isInteger(this.sliderMax) && + !defaultFilterData.hasFloatValues; // Integer columns step by whole units (discrete counts). Float columns use // "any" — a continuous control with no value grid, so the column max (and // any high-decimal value) stays exactly selectable via both the slider and // the number box, at any column magnitude. A fixed absolute step floored // the reachable max below the true max and broke selection of the top node. - this.stepSize = allInteger ? this.cache.CFG.FILTER_STEP_SIZE_INTEGER : "any"; + this.stepSize = allInteger ? this.cache.CFG.FILTER_STEP_SIZE_INTEGER : 'any'; this.initializeIds(); this.inputStart = null; this.inputEnd = null; @@ -274,7 +293,9 @@ class InvertibleRangeSlider { this.currentMax = 1; this.isInverted = false; } else { - let filterData = this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(this.propID); + let filterData = this.cache.data.layouts[this.cache.data.selectedLayout].filters.get( + this.propID + ); this.currentMin = filterData.lowerThreshold; this.currentMax = filterData.upperThreshold; this.isInverted = filterData.isInverted; @@ -283,7 +304,9 @@ class InvertibleRangeSlider { writeCurrentFilterSettings() { if (this.cache.data.layouts[this.cache.data.selectedLayout].filters.has(this.propID)) { - let filterData = this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(this.propID); + let filterData = this.cache.data.layouts[this.cache.data.selectedLayout].filters.get( + this.propID + ); filterData.lowerThreshold = this.currentMin; filterData.upperThreshold = this.currentMax; filterData.isInverted = this.isInverted; @@ -323,8 +346,10 @@ class InvertibleRangeSlider { const r = this.track.getBoundingClientRect(); if (r.width === 0) return; // not laid out yet const midY = r.top + r.height / 2; - const lw = this.signLeft.offsetWidth, lh = this.signLeft.offsetHeight; - const rw = this.signRight.offsetWidth, rh = this.signRight.offsetHeight; + const lw = this.signLeft.offsetWidth, + lh = this.signLeft.offsetHeight; + const rw = this.signRight.offsetWidth, + rh = this.signRight.offsetHeight; this.signLeft.style.top = `${midY - lh / 2}px`; this.signLeft.style.left = `${Math.max(2, r.left - lw - 8)}px`; this.signRight.style.top = `${midY - rh / 2}px`; @@ -332,7 +357,7 @@ class InvertibleRangeSlider { } createSliderInput(id, initialValue, relatedSliderId) { - const input = document.createElement("input"); + const input = document.createElement('input'); input.id = id; input.value = initialValue; input.addEventListener('keydown', (ev) => { @@ -347,8 +372,8 @@ class InvertibleRangeSlider { input.value = sliderElem.value; } else { sliderElem.value = newValue; - sliderElem.dispatchEvent(new Event("input")); - sliderElem.dispatchEvent(new Event("change")); + sliderElem.dispatchEvent(new Event('input')); + sliderElem.dispatchEvent(new Event('change')); } } }); @@ -367,12 +392,16 @@ class InvertibleRangeSlider { appendTo(parent) { if (this.cache.CFG.HIDE_SLIDERS_WITH_SAME_MIN_MAX_VALUES && this.sliderMin === this.sliderMax) { - parent.appendChild(document.createElement("span")); + // A numeric property whose only value is min === max has no range to + // filter, so a slider (and its exact-value inputs) would be inert. Show a + // compact read-only badge with the value instead; nothing is rendered for + // Details mode to reveal, so it stays a plain checkmark + value. + parent.appendChild(this.createSingleValueIndicator()); return false; } this.isValidSlider = true; - const div = document.createElement("div"); + const div = document.createElement('div'); div.innerHTML = this.createDivInnerHTML(); const slider = div.firstElementChild; slider.style.width = '100%'; @@ -383,10 +412,18 @@ class InvertibleRangeSlider { // sync with the handles via handleThresholdOnInputEvent (writes their values). const inputRow = document.createElement('div'); inputRow.className = 'filter-input-row'; - this.inputStart = this.createSliderInput(this.sliderIdStartInput, this.currentMin, this.sliderIdStart); - this.inputStart.title = "Lower threshold — type an exact value and press Enter"; - this.inputEnd = this.createSliderInput(this.sliderIdEndInput, this.currentMax, this.sliderIdEnd); - this.inputEnd.title = "Upper threshold — type an exact value and press Enter"; + this.inputStart = this.createSliderInput( + this.sliderIdStartInput, + this.currentMin, + this.sliderIdStart + ); + this.inputStart.title = 'Lower threshold — type an exact value and press Enter'; + this.inputEnd = this.createSliderInput( + this.sliderIdEndInput, + this.currentMax, + this.sliderIdEnd + ); + this.inputEnd.title = 'Upper threshold — type an exact value and press Enter'; inputRow.appendChild(this.inputStart); inputRow.appendChild(this.inputEnd); @@ -394,6 +431,24 @@ class InvertibleRangeSlider { parent.appendChild(inputRow); } + createSingleValueIndicator() { + const badge = document.createElement('span'); + badge.className = 'filter-single-value'; + const value = StaticUtilities.formatNumber( + this.sliderMin, + this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION + ); + const check = document.createElement('span'); + check.className = 'filter-single-value-check'; + check.textContent = '✓'; + check.setAttribute('aria-hidden', 'true'); + const valueSpan = document.createElement('span'); + valueSpan.textContent = value; + badge.append(check, valueSpan); + badge.title = `Single value (${value}) for ${StaticUtilities.formatPropsAsTree(this.propID)} — toggle this property to include or exclude nodes that have it; there is just no range to narrow.`; + return badge; + } + createDivInnerHTML() { return `
@@ -441,30 +496,36 @@ class InvertibleRangeSlider { this.sliderEnd.dispatchEvent(new Event('change')); }); - this.sliderStart.addEventListener("input", () => { + this.sliderStart.addEventListener('input', () => { if (this.cache.EVENT_LOCKS.FILTERS_LOCKED_BY_MANUAL_QUERY) return; this.handleThresholdOnInputEvent(true); }); - this.sliderStart.addEventListener("change", async () => { + this.sliderStart.addEventListener('change', async () => { if (this.cache.EVENT_LOCKS.FILTERS_LOCKED_BY_MANUAL_QUERY) return; try { this.writeCurrentFilterSettings(); - await this.cache.fm.handleFilterEvent("Filtering", - `Applying lower threshold ${this.sliderStart.value} for ${this.propID}`, this.propID); + await this.cache.fm.handleFilterEvent( + 'Filtering', + `Applying lower threshold ${this.sliderStart.value} for ${this.propID}`, + this.propID + ); } catch (err) { this.cache.ui.error(`Failed to apply lower threshold: ${err.message}`); } }); - this.sliderEnd.addEventListener("input", () => { + this.sliderEnd.addEventListener('input', () => { if (this.cache.EVENT_LOCKS.FILTERS_LOCKED_BY_MANUAL_QUERY) return; this.handleThresholdOnInputEvent(false); }); - this.sliderEnd.addEventListener("change", async () => { + this.sliderEnd.addEventListener('change', async () => { if (this.cache.EVENT_LOCKS.FILTERS_LOCKED_BY_MANUAL_QUERY) return; try { this.writeCurrentFilterSettings(); - await this.cache.fm.handleFilterEvent("Filtering", - `Applying upper threshold ${this.sliderEnd.value} for ${this.propID}`, this.propID); + await this.cache.fm.handleFilterEvent( + 'Filtering', + `Applying upper threshold ${this.sliderEnd.value} for ${this.propID}`, + this.propID + ); } catch (err) { this.cache.ui.error(`Failed to apply upper threshold: ${err.message}`); } @@ -488,45 +549,69 @@ class InvertibleRangeSlider { const leftWidth = this.calcPercentage(isLower ? secondaryValue : primaryValue); const rightWidth = this.calcPercentage(isLower ? primaryValue : secondaryValue); this.inverseLeft.style.width = leftWidth + '%'; - this.inverseRight.style.width = (100 - rightWidth) + '%'; + this.inverseRight.style.width = 100 - rightWidth + '%'; this.range.style.left = '50%'; this.inverseLeft.style.backgroundColor = '#C33D35'; this.inverseRight.style.backgroundColor = '#C33D35'; if (isLower) { - this.labelEnd.innerHTML = StaticUtilities.formatNumber(primaryValue, this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION); - this.labelStart.innerHTML = StaticUtilities.formatNumber(secondaryValue, this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION); + this.labelEnd.innerHTML = StaticUtilities.formatNumber( + primaryValue, + this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION + ); + this.labelStart.innerHTML = StaticUtilities.formatNumber( + secondaryValue, + this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION + ); } else { - this.labelStart.innerHTML = StaticUtilities.formatNumber(primaryValue, this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION); - this.labelEnd.innerHTML = StaticUtilities.formatNumber(secondaryValue, this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION); + this.labelStart.innerHTML = StaticUtilities.formatNumber( + primaryValue, + this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION + ); + this.labelEnd.innerHTML = StaticUtilities.formatNumber( + secondaryValue, + this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION + ); } - this.labelStart.parentElement.classList.add("flipped"); - this.labelEnd.parentElement.classList.add("flipped"); + this.labelStart.parentElement.classList.add('flipped'); + this.labelEnd.parentElement.classList.add('flipped'); - this.inputStart.classList.add("red"); - this.inputEnd.classList.add("red"); + this.inputStart.classList.add('red'); + this.inputEnd.classList.add('red'); } else { const leftPos = this.calcPercentage(isLower ? primaryValue : secondaryValue); const rightPos = 100 - this.calcPercentage(isLower ? secondaryValue : primaryValue); this.range.style.left = leftPos + '%'; - this.range.style.width = (100 - leftPos - rightPos) + '%'; + this.range.style.width = 100 - leftPos - rightPos + '%'; this.inverseLeft.style.width = leftPos + '%'; this.inverseRight.style.width = rightPos + '%'; this.inverseLeft.style.backgroundColor = 'grey'; this.inverseRight.style.backgroundColor = 'grey'; if (isLower) { - this.labelStart.innerHTML = StaticUtilities.formatNumber(primaryValue, this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION); - this.labelEnd.innerHTML = StaticUtilities.formatNumber(secondaryValue, this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION); + this.labelStart.innerHTML = StaticUtilities.formatNumber( + primaryValue, + this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION + ); + this.labelEnd.innerHTML = StaticUtilities.formatNumber( + secondaryValue, + this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION + ); } else { - this.labelStart.innerHTML = StaticUtilities.formatNumber(secondaryValue, this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION); - this.labelEnd.innerHTML = StaticUtilities.formatNumber(primaryValue, this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION); + this.labelStart.innerHTML = StaticUtilities.formatNumber( + secondaryValue, + this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION + ); + this.labelEnd.innerHTML = StaticUtilities.formatNumber( + primaryValue, + this.cache.CFG.FILTER_VISUAL_FLOAT_PRECISION + ); } - this.labelStart.parentElement.classList.remove("flipped"); - this.labelEnd.parentElement.classList.remove("flipped"); + this.labelStart.parentElement.classList.remove('flipped'); + this.labelEnd.parentElement.classList.remove('flipped'); - this.inputStart.classList.remove("red"); - this.inputEnd.classList.remove("red"); + this.inputStart.classList.remove('red'); + this.inputEnd.classList.remove('red'); } if (isLower) { @@ -545,19 +630,27 @@ class InvertibleRangeSlider { const clampedMax = Math.min(Math.max(max, this.sliderMin), this.sliderMax); if (!inverted && min > max) { - this.cache.ui.error(`Cannot set min threshold to ${min} and max threshold to ${max} for ${this.propID}`); + this.cache.ui.error( + `Cannot set min threshold to ${min} and max threshold to ${max} for ${this.propID}` + ); return; } if (inverted && max < min) { - this.cache.ui.error(`Cannot set threshold to LOWER THAN ${min} OR GREATER THAN ${max} for inverted ${this.propID}`); + this.cache.ui.error( + `Cannot set threshold to LOWER THAN ${min} OR GREATER THAN ${max} for inverted ${this.propID}` + ); return; } if (min < this.sliderMin) { - this.cache.ui.warning(`Minimum threshold for ${this.propID} corrected to ${clampedMin} (from ${min})`); + this.cache.ui.warning( + `Minimum threshold for ${this.propID} corrected to ${clampedMin} (from ${min})` + ); } if (max > this.sliderMax) { - this.cache.ui.warning(`Maximum threshold for ${this.propID} corrected to ${clampedMax} (from ${max})`); + this.cache.ui.warning( + `Maximum threshold for ${this.propID} corrected to ${clampedMax} (from ${max})` + ); } this.sliderStart.value = inverted ? clampedMax : clampedMin; @@ -577,35 +670,41 @@ class UIComponentManager { buildDropdownOptions() { let selectViewDropdown = document.getElementById('selectView'); - let selectViewOptions = Object.keys(this.cache.data.layouts).map(key => { - let selected = this.cache.data.selectedLayout === key ? "selected" : ""; + let selectViewOptions = Object.keys(this.cache.data.layouts).map((key) => { + let selected = this.cache.data.selectedLayout === key ? 'selected' : ''; return ``; }); - selectViewDropdown.innerHTML = selectViewOptions.join(""); + selectViewDropdown.innerHTML = selectViewOptions.join(''); } createSectionToggleButton(enable, section, subSection = null) { - const btn = document.createElement("button"); - btn.className = "small-btn toggle-section-btn ml-1"; - if (subSection) btn.classList.add("extra-small"); - btn.textContent = enable ? "✔" : "✗"; - btn.title = `${enable ? 'Enable' : 'Disable'} all filters for the ${subSection - ? 'group: ' + "\n └─ " + section + "\n └─ " + subSection - : 'section: ' + "\n └─ " + section}`; + const btn = document.createElement('button'); + btn.className = 'small-btn toggle-section-btn ml-1'; + if (subSection) btn.classList.add('extra-small'); + btn.textContent = enable ? '✔' : '✗'; + btn.title = `${enable ? 'Enable' : 'Disable'} all filters for the ${ + subSection + ? 'group: ' + '\n └─ ' + section + '\n └─ ' + subSection + : 'section: ' + '\n └─ ' + section + }`; btn.onclick = async () => { - subSection ? await this.cache.ui.toggleSubSection(enable, section, subSection) : await this.cache.ui.toggleSection(enable, section); + subSection + ? await this.cache.ui.toggleSubSection(enable, section, subSection) + : await this.cache.ui.toggleSection(enable, section); }; return btn; } createSectionResetButton(section, subSection = undefined) { - const btn = document.createElement("button"); - btn.className = "small-btn toggle-section-btn ml-1"; - if (subSection) btn.classList.add("extra-small"); - btn.textContent = "⟳"; - btn.title = `Reset all filters for the ${subSection - ? 'group to their default values: ' + "\n └─ " + section + "\n └─ " + subSection - : 'section to their default values: ' + "\n └─ " + section}`; + const btn = document.createElement('button'); + btn.className = 'small-btn toggle-section-btn ml-1'; + if (subSection) btn.classList.add('extra-small'); + btn.textContent = '⟳'; + btn.title = `Reset all filters for the ${ + subSection + ? 'group to their default values: ' + '\n └─ ' + section + '\n └─ ' + subSection + : 'section to their default values: ' + '\n └─ ' + section + }`; btn.onclick = async () => { await this.cache.fm.resetFilters(section, subSection); }; @@ -616,28 +715,36 @@ class UIComponentManager { const circleButton = document.createElement('div'); circleButton.className = `circle-button`; - for (let [group, quadrantPosition] of Object.entries(DEFAULTS.BUBBLE_GROUP_QUADRANT_POSITIONS)) { + for (let [group, quadrantPosition] of Object.entries( + DEFAULTS.BUBBLE_GROUP_QUADRANT_POSITIONS + )) { const quadrant = document.createElement('button'); - quadrant.classList.add("quadrant"); + quadrant.classList.add('quadrant'); quadrant.classList.add(quadrantPosition); - this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID)[`${group}Members`].size === 0 ? quadrant.classList.remove("active") : quadrant.classList.add("active"); + this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID)[`${group}Members`] + .size === 0 + ? quadrant.classList.remove('active') + : quadrant.classList.add('active'); quadrant.addEventListener('click', async () => { try { - let shouldShowRemove = quadrant.classList.contains("active"); - let members = this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID)[`${group}Members`]; + let shouldShowRemove = quadrant.classList.contains('active'); + let members = + this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID)[ + `${group}Members` + ]; if (shouldShowRemove) { this.cache.data.layouts[this.cache.data.selectedLayout][`${group}Props`].delete(propID); quadrant.title = `Remove ${propID} from ${group}.`; members.delete(propID); - quadrant.classList.remove("active"); + quadrant.classList.remove('active'); await this.cache.gcm.decideToRenderOrDraw(); } else { this.cache.data.layouts[this.cache.data.selectedLayout][`${group}Props`].add(propID); quadrant.title = `Highlight ${propID} and add to bubble-group (${group})`; members.add(propID); - quadrant.classList.add("active"); + quadrant.classList.add('active'); await this.cache.gcm.decideToRenderOrDraw(); } } catch (err) { @@ -657,12 +764,14 @@ class UIComponentManager { circleButton.className = `circle-button-compact`; circleButton.id = 'manualBubbleGroupButton'; - for (let [group, quadrantPosition] of Object.entries(DEFAULTS.BUBBLE_GROUP_QUADRANT_POSITIONS)) { + for (let [group, quadrantPosition] of Object.entries( + DEFAULTS.BUBBLE_GROUP_QUADRANT_POSITIONS + )) { const quadrant = document.createElement('button'); - quadrant.classList.add("quadrant"); + quadrant.classList.add('quadrant'); quadrant.classList.add(quadrantPosition); - quadrant.classList.add("manual"); - quadrant.classList.add("compact"); + quadrant.classList.add('manual'); + quadrant.classList.add('compact'); quadrant.addEventListener('click', async () => { try { @@ -687,7 +796,7 @@ class UIComponentManager { return `
- ${isEdge ? "Edge" : "Node"} + ${isEdge ? 'Edge' : 'Node'}
${title}
${subtitle} @@ -717,11 +826,13 @@ class UIComponentManager { 📊

-
` +
`; } } - const item = isEdge ? this.cache.edgeRef.get(nodeOrEdgeID) : this.cache.nodeRef.get(nodeOrEdgeID); + const item = isEdge + ? this.cache.edgeRef.get(nodeOrEdgeID) + : this.cache.nodeRef.get(nodeOrEdgeID); let tooltip = initAndAddHeader(); addDescription(); addMetric(); @@ -741,17 +852,17 @@ class UIComponentManager { * Ensures a section object and subSection array exist, then pushes a property item. */ function pushSubSectionProperty(secName, subName, prop, val) { - let sectionObj = structuredData.find(s => s.section === secName); + let sectionObj = structuredData.find((s) => s.section === secName); if (!sectionObj) { - sectionObj = {section: secName, subSections: []}; + sectionObj = { section: secName, subSections: [] }; structuredData.push(sectionObj); } - let subObj = sectionObj.subSections.find(sub => sub.name === subName); + let subObj = sectionObj.subSections.find((sub) => sub.name === subName); if (!subObj) { - subObj = {name: subName, props: []}; + subObj = { name: subName, props: [] }; sectionObj.subSections.push(subObj); } - subObj.props.push({key: prop, value: val}); + subObj.props.push({ key: prop, value: val }); } // Gather valid properties, grouped @@ -787,15 +898,14 @@ class UIComponentManager { function flattenBlocks() { const blocks = []; for (const s of structuredData) { - blocks.push({type: "section", text: s.section}); + blocks.push({ type: 'section', text: s.section }); for (const sb of s.subSections) { - blocks.push({type: "subSection", section: s.section, text: sb.name, props: sb.props}); + blocks.push({ type: 'subSection', section: s.section, text: sb.name, props: sb.props }); } } return blocks; } - const orderedBlocks = flattenBlocks(); // If we have nothing to show, return the basic tooltip @@ -817,7 +927,6 @@ class UIComponentManager { // 5) Build the tooltip HTML // ------------------ function buildColumns() { - tooltip += `
`; for (const col of columns) { @@ -825,13 +934,13 @@ class UIComponentManager { let startedList = false; for (const block of col) { - if (block.type === "section") { + if (block.type === 'section') { // Close a list if it's open before starting a new section if (startedList) { tooltip += ``; startedList = false; } - } else if (block.type === "subSection") { + } else if (block.type === 'subSection') { if (startedList) { tooltip += ``; startedList = false; @@ -865,10 +974,13 @@ class UIComponentManager { wrapper.className = 'checkboxWrapper'; wrapper.id = `filter-${propID}-checkbox-wrapper`; - const input = this.cache.uiComponents.createCheckboxInput(propID, this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID).active); + const input = this.cache.uiComponents.createCheckboxInput( + propID, + this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID).active + ); const customCheckbox = this.cache.uiComponents.createCustomCheckbox(propID); const actionButton = this.cache.uiComponents.createAddToQueryButton(propID); - const displayField = document.createElement("span"); + const displayField = document.createElement('span'); displayField.className = 'checkboxLabel'; displayField.textContent = prop; @@ -882,10 +994,14 @@ class UIComponentManager { wrapper.addEventListener('change', async () => { if (this.cache.EVENT_LOCKS.FILTERS_LOCKED_BY_MANUAL_QUERY) return; try { - this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID).active = input.checked; + this.cache.data.layouts[this.cache.data.selectedLayout].filters.get(propID).active = + input.checked; input.checked ? this.cache.activeProps.add(propID) : this.cache.activeProps.delete(propID); - let status = input.checked ? "Showing" : "Hiding"; - await this.cache.fm.handleFilterEvent(`${status} Elements`, `Nodes and related edges for ${propID}`); + let status = input.checked ? 'Showing' : 'Hiding'; + await this.cache.fm.handleFilterEvent( + `${status} Elements`, + `Nodes and related edges for ${propID}` + ); } catch (err) { this.cache.ui.error(`Failed to toggle filter: ${err.message}`); } @@ -918,7 +1034,7 @@ class UIComponentManager { createCustomCheckbox(propID) { const customCheckbox = document.createElement('span'); customCheckbox.id = `filter-${propID}-checkbox-inner`; - customCheckbox.className = "checkbox checkbox-green"; + customCheckbox.className = 'checkbox checkbox-green'; return customCheckbox; } @@ -945,20 +1061,21 @@ class UIComponentManager { } } else if (dropdown) { if (this.cache.CFG.QUERY_BTN_USE_CURRENT_FILTER) { - queryFragment = `${propID} IN [${[...dropdown.selectedCategories].map(cat => StaticUtilities.escapeQueryValue(cat)).join(",")}]` + queryFragment = `${propID} IN [${[...dropdown.selectedCategories].map((cat) => StaticUtilities.escapeQueryValue(cat)).join(',')}]`; } else { - queryFragment = `${propID} IN [${[...dropdown.categories].map(cat => StaticUtilities.escapeQueryValue(cat)).join(",")}]` + queryFragment = `${propID} IN [${[...dropdown.categories].map((cat) => StaticUtilities.escapeQueryValue(cat)).join(',')}]`; } } - if (this.cache.data.layouts[this.cache.data.selectedLayout]["query"] === undefined) { + if (this.cache.data.layouts[this.cache.data.selectedLayout]['query'] === undefined) { this.cache.qm.handleQueryValidationEvent(true); } if (!this.cache.query.text.textContent.trim()) { - this.cache.data.layouts[this.cache.data.selectedLayout]["query"] = `(${queryFragment})`; + this.cache.data.layouts[this.cache.data.selectedLayout]['query'] = `(${queryFragment})`; } else { - this.cache.data.layouts[this.cache.data.selectedLayout]["query"] += ` OR (${queryFragment})`; + this.cache.data.layouts[this.cache.data.selectedLayout]['query'] += + ` OR (${queryFragment})`; } this.cache.qm.updateQueryTextArea(); }); @@ -971,8 +1088,8 @@ class UIComponentManager { } createAddOrRemoveToSelectionGroup(propID) { - const group = document.createElement("div"); - group.classList.add("pm-group"); + const group = document.createElement('div'); + group.classList.add('pm-group'); const addBtn = this.createAddOrRemoveToSelectionButton(propID, true); const removeBtn = this.createAddOrRemoveToSelectionButton(propID, false); @@ -983,14 +1100,14 @@ class UIComponentManager { } createAddOrRemoveToSelectionButton(propID, shouldAdd) { - const btn = document.createElement("button"); - btn.classList.add("plus-minus-button"); - btn.textContent = shouldAdd ? "+" : "-"; - btn.title = shouldAdd ? "Add to selection" : "Remove from selection"; - btn.addEventListener("click", async () => { + const btn = document.createElement('button'); + btn.classList.add('plus-minus-button'); + btn.textContent = shouldAdd ? '+' : '-'; + btn.title = shouldAdd ? 'Add to selection' : 'Remove from selection'; + btn.addEventListener('click', async () => { try { if (!this.cache.graph) { - this.cache.ui.warning("Please wait for graph to initialize"); + this.cache.ui.warning('Please wait for graph to initialize'); return; } @@ -1013,4 +1130,4 @@ class UIComponentManager { } } -export {DropdownChecklist, InvertibleRangeSlider, UIComponentManager}; +export { DropdownChecklist, InvertibleRangeSlider, UIComponentManager }; diff --git a/src/style.css b/src/style.css index 6aa57e3..adb6a1c 100644 --- a/src/style.css +++ b/src/style.css @@ -1495,6 +1495,27 @@ h5 { box-shadow: 0 0 0 2px rgba(195, 61, 53, 0.25); } +/* Single-value fallback shown in place of a slider when a numeric property has + only one possible value (min === max). Read-only checkmark + value. */ +.filter-single-value { + display: inline-flex; + align-items: center; + gap: 5px; + align-self: flex-start; + height: 20px; + padding: 0 8px; + font-size: 11px; + color: var(--text-muted); + background: var(--surface-3); + border: 1px solid var(--border-soft); + border-radius: 4px; +} + +.filter-single-value-check { + color: var(--accent-text); + font-weight: 700; +} + .filter-row-col3 { flex: 0 0 auto; display: flex; diff --git a/tests/filter-single-value.test.js b/tests/filter-single-value.test.js new file mode 100644 index 0000000..5ae03d4 --- /dev/null +++ b/tests/filter-single-value.test.js @@ -0,0 +1,87 @@ +// @vitest-environment jsdom +import { describe, it, expect, beforeEach } from 'vitest'; +import { InvertibleRangeSlider } from '../src/managers/ui_components.js'; +import { CFG, DEFAULTS } from '../src/config.js'; + +// ========================================================================== +// Single-value numeric properties (min === max) have no range to filter, so a +// slider is inert. They should fall back to a read-only checkmark + value +// badge instead, and render no exact-value inputs (nothing for Details mode to +// reveal). +// ========================================================================== + +function createMockCache(filterDefaults) { + return { + CFG, + DEFAULTS, + data: { + filterDefaults: new Map(Object.entries(filterDefaults)), + selectedLayout: 0, + layouts: [{ filters: new Map() }], + }, + propIDToInvertibleRangeSliders: new Map(), + }; +} + +describe('InvertibleRangeSlider — single-value fallback', () => { + let parent; + + beforeEach(() => { + parent = document.createElement('div'); + }); + + it('renders a checkmark + value badge instead of a slider when min === max', () => { + const cache = createMockCache({ + 'prop::sub::single': { lowerThreshold: 50, upperThreshold: 50, hasFloatValues: false }, + }); + const slider = new InvertibleRangeSlider('prop::sub::single', cache); + + slider.appendTo(parent); + + const badge = parent.querySelector('.filter-single-value'); + expect(badge).not.toBeNull(); + expect(badge.querySelector('.filter-single-value-check').textContent).toBe('✓'); + expect(badge.textContent).toContain('50'); + // appendListeners is a no-op for a non-rendered slider. + expect(slider.isValidSlider).toBeFalsy(); + }); + + it('omits the exact-value input row for single-value properties', () => { + const cache = createMockCache({ + 'prop::sub::single': { lowerThreshold: 3.14, upperThreshold: 3.14, hasFloatValues: true }, + }); + const slider = new InvertibleRangeSlider('prop::sub::single', cache); + + slider.appendTo(parent); + + expect(parent.querySelector('.filter-input-row')).toBeNull(); + expect(parent.querySelector('input[type="range"]')).toBeNull(); + }); + + it('renders the slider and input row when min !== max', () => { + const cache = createMockCache({ + 'prop::sub::range': { lowerThreshold: 0, upperThreshold: 10, hasFloatValues: false }, + }); + const slider = new InvertibleRangeSlider('prop::sub::range', cache); + + slider.appendTo(parent); + + expect(parent.querySelector('.filter-single-value')).toBeNull(); + expect(parent.querySelector('.filter-input-row')).not.toBeNull(); + expect(parent.querySelectorAll('input[type="range"]').length).toBe(2); + expect(slider.isValidSlider).toBe(true); + }); + + it('keeps the meaningless slider when the hide flag is disabled', () => { + const cache = createMockCache({ + 'prop::sub::single': { lowerThreshold: 50, upperThreshold: 50, hasFloatValues: false }, + }); + cache.CFG = { ...CFG, HIDE_SLIDERS_WITH_SAME_MIN_MAX_VALUES: false }; + const slider = new InvertibleRangeSlider('prop::sub::single', cache); + + slider.appendTo(parent); + + expect(parent.querySelector('.filter-single-value')).toBeNull(); + expect(parent.querySelectorAll('input[type="range"]').length).toBe(2); + }); +}); diff --git a/tests/hide-disconnected-filter.test.js b/tests/hide-disconnected-filter.test.js new file mode 100644 index 0000000..1f7ca77 --- /dev/null +++ b/tests/hide-disconnected-filter.test.js @@ -0,0 +1,181 @@ +// @vitest-environment jsdom +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +// -------------------------------------------------------------------------- +// Regression: "hide disconnected nodes" must survive filter changes. +// +// Bug: preRenderEvent rebuilt nodeIDsToBeShown purely from filters while the +// dangling set was a separate, stale layer. A previously-hidden dangling node +// that still passed the filter landed in BOTH idsToShow and idsToHide, and +// updateElementVisibility's show-then-hide diff resurfaced it on every filter +// change. Newly-dangling nodes (whose only edge got filtered out) were never +// detected either. Fix: recompute the dangling set against the fresh filtered +// view inside preRenderEvent and exclude it from idsToShow. +// -------------------------------------------------------------------------- + +const { GraphCoreManager } = await import('../src/graph/core.js'); + +// A node/edge the AST "passes" — no real features, so testNode is never +// consulted (features.size === 0 short-circuits to always-visible). We drive +// connectivity purely through the adjacency maps below. +function makeNode(id) { + return { id, features: new Set(), featureIsWithinThreshold: new Map() }; +} +// Edges carry a feature so the AST's testEdge actually gates them — a +// featureless edge is unconditionally visible (preRenderEvent short-circuit). +function makeEdge(id, source, target) { + return { + id, + source, + target, + features: new Set(['weight']), + featureIsWithinThreshold: new Map(), + }; +} + +// Build a cache whose visible edge set is dictated by `visibleEdgeIds`, so a +// "filter change" is modelled as flipping which edges pass. +function makeCache({ nodes, edges, hideDisconnected, visibleEdgeIds }) { + const nodeRef = new Map(nodes.map((n) => [n.id, n])); + const edgeRef = new Map(edges.map((e) => [e.id, e])); + + const nodeIDToEdgeIDs = new Map(nodes.map((n) => [n.id, new Set()])); + const edgeIDToNodeIDs = new Map(); + for (const e of edges) { + edgeIDToNodeIDs.set(e.id, new Set([e.source, e.target])); + nodeIDToEdgeIDs.get(e.source).add(e.id); + nodeIDToEdgeIDs.get(e.target).add(e.id); + } + + const captured = {}; + return { + cache: { + styleChanged: false, + bubbleSetChanged: false, + EVENT_LOCKS: { + QUERY_UPDATE_EVENT: false, + FILTERS_LOCKED_BY_MANUAL_QUERY: false, + }, + data: { + selectedLayout: 'Default', + layouts: { Default: { hideDisconnectedNodes: hideDisconnected } }, + }, + nodeRef, + edgeRef, + nodeIDToEdgeIDs, + edgeIDToNodeIDs, + nodeIDsToBeShown: new Set(), + edgeIDsToBeShown: new Set(), + propIDsToNodeIDsToBeShown: new Map(), + propIDsToEdgeIDsToBeShown: new Map(), + remainingEdgeRelatedNodes: new Set(), + hiddenDanglingNodeIDs: new Set(), + hiddenDanglingEdgeIDs: new Set(), + qm: { + resetQuery: vi.fn(), + decodeQueryAndBuildAST: vi.fn(), + storeQuery: vi.fn(), + }, + // No node features -> testNode never runs; edges pass only if listed. + query: { + ast: { + testNode: () => true, + testEdge: (edge) => visibleEdgeIds.has(edge.id), + }, + }, + fm: { + resetFeatureIsWithinThresholdMaps: vi.fn(), + updateElementVisibility: vi.fn(async (idsToShow, idsToHide) => { + captured.idsToShow = idsToShow; + captured.idsToHide = idsToHide; + }), + }, + bs: { updateBubbleSetIfChanged: vi.fn(async () => {}) }, + }, + captured, + }; +} + +beforeEach(() => { + document.body.innerHTML = ''; +}); + +describe('hide disconnected nodes survives filter changes', () => { + // n1-n2 connected via e1; n3 has no edges (always dangling). + const baseGraph = () => ({ + nodes: [makeNode('n1'), makeNode('n2'), makeNode('n3')], + edges: [makeEdge('e1', 'n1', 'n2')], + }); + + it('hides the disconnected node and excludes it from idsToShow', async () => { + const { cache, captured } = makeCache({ + ...baseGraph(), + hideDisconnected: true, + visibleEdgeIds: new Set(['e1']), + }); + const gcm = new GraphCoreManager(cache); + + await gcm.preRenderEvent(); + + expect(cache.hiddenDanglingNodeIDs.has('n3')).toBe(true); + expect(captured.idsToShow).not.toContain('n3'); + expect(captured.idsToShow).toEqual(expect.arrayContaining(['n1', 'n2', 'e1'])); + expect(captured.idsToHide).toContain('n3'); + }); + + it('never lists the same id in both idsToShow and idsToHide', async () => { + const { cache, captured } = makeCache({ + ...baseGraph(), + hideDisconnected: true, + visibleEdgeIds: new Set(['e1']), + }); + const gcm = new GraphCoreManager(cache); + + // First pass hides n3. Second pass models a subsequent filter change. + await gcm.preRenderEvent(); + await gcm.preRenderEvent(); + + const overlap = captured.idsToShow.filter((id) => captured.idsToHide.includes(id)); + expect(overlap).toEqual([]); + expect(captured.idsToShow).not.toContain('n3'); + }); + + it('re-detects nodes that become dangling after a filter removes their edge', async () => { + // Start with e1 visible: n1, n2 connected, only n3 dangling. + const graph = baseGraph(); + const { cache, captured } = makeCache({ + ...graph, + hideDisconnected: true, + visibleEdgeIds: new Set(['e1']), + }); + const gcm = new GraphCoreManager(cache); + await gcm.preRenderEvent(); + expect(cache.hiddenDanglingNodeIDs).toEqual(new Set(['n3'])); + + // Filter change: e1 no longer passes -> n1 and n2 are now dangling too. + cache.query.ast.testEdge = () => false; + await gcm.preRenderEvent(); + + expect(cache.hiddenDanglingNodeIDs).toEqual(new Set(['n1', 'n2', 'n3'])); + expect(captured.idsToShow).toEqual([]); + }); + + it('shows everything again once the flag is turned off', async () => { + const { cache, captured } = makeCache({ + ...baseGraph(), + hideDisconnected: true, + visibleEdgeIds: new Set(['e1']), + }); + const gcm = new GraphCoreManager(cache); + await gcm.preRenderEvent(); + expect(cache.hiddenDanglingNodeIDs.has('n3')).toBe(true); + + // User clears the toggle, then changes a filter. + cache.data.layouts.Default.hideDisconnectedNodes = false; + await gcm.preRenderEvent(); + + expect(cache.hiddenDanglingNodeIDs.size).toBe(0); + expect(captured.idsToShow).toContain('n3'); + expect(captured.idsToHide).not.toContain('n3'); + }); +});