From 117382582a3d8daa1ec3d98c95ab70c88fa08d9f Mon Sep 17 00:00:00 2001 From: "Guilherme D. Garcia" Date: Fri, 26 Jun 2026 01:24:22 -0400 Subject: [PATCH] phonokit:0.5.11 --- packages/preview/phonokit/0.5.11/LICENSE | 21 + packages/preview/phonokit/0.5.11/README.md | 176 ++ packages/preview/phonokit/0.5.11/_config.typ | 8 + .../preview/phonokit/0.5.11/autosegmental.typ | 275 +++ .../preview/phonokit/0.5.11/consonants.typ | 938 +++++++ packages/preview/phonokit/0.5.11/ex.typ | 408 +++ packages/preview/phonokit/0.5.11/extras.typ | 39 + packages/preview/phonokit/0.5.11/features.typ | 2137 ++++++++++++++++ .../0.5.11/gallery/autoseg_example_1.png | Bin 0 -> 5349 bytes .../0.5.11/gallery/autoseg_example_1.typ | 11 + .../0.5.11/gallery/autoseg_example_2.png | Bin 0 -> 11274 bytes .../0.5.11/gallery/autoseg_example_2.typ | 22 + .../0.5.11/gallery/autoseg_example_3.png | Bin 0 -> 57914 bytes .../0.5.11/gallery/autoseg_example_3.typ | 47 + .../0.5.11/gallery/consonants_example.png | Bin 0 -> 103818 bytes .../0.5.11/gallery/consonants_example.typ | 8 + .../phonokit/0.5.11/gallery/feat_geom.png | Bin 0 -> 45066 bytes .../phonokit/0.5.11/gallery/feat_geom.typ | 9 + .../0.5.11/gallery/features_example.png | Bin 0 -> 127367 bytes .../0.5.11/gallery/features_example.typ | 5 + .../phonokit/0.5.11/gallery/grid_example.png | Bin 0 -> 5390 bytes .../phonokit/0.5.11/gallery/grid_example.typ | 4 + .../phonokit/0.5.11/gallery/ipa_example.png | Bin 0 -> 28255 bytes .../phonokit/0.5.11/gallery/ipa_example.typ | 17 + .../0.5.11/gallery/maxent_example.png | Bin 0 -> 131582 bytes .../0.5.11/gallery/maxent_example.typ | 41 + .../0.5.11/gallery/multi-tier_example.png | Bin 0 -> 17166 bytes .../0.5.11/gallery/multi-tier_example.typ | 18 + .../phonokit/0.5.11/gallery/ot_example.png | Bin 0 -> 22397 bytes .../phonokit/0.5.11/gallery/ot_example.typ | 17 + .../0.5.11/gallery/syllable_example.png | Bin 0 -> 8740 bytes .../0.5.11/gallery/syllable_example.typ | 6 + .../0.5.11/gallery/vowels_example.png | Bin 0 -> 24100 bytes .../0.5.11/gallery/vowels_example.typ | 17 + .../phonokit/0.5.11/gallery/word_example.png | Bin 0 -> 22635 bytes .../phonokit/0.5.11/gallery/word_example.typ | 5 + packages/preview/phonokit/0.5.11/geom.typ | 2029 +++++++++++++++ packages/preview/phonokit/0.5.11/grids.typ | 114 + packages/preview/phonokit/0.5.11/hasse.typ | 460 ++++ .../preview/phonokit/0.5.11/intonational.typ | 80 + packages/preview/phonokit/0.5.11/ipa.typ | 412 +++ packages/preview/phonokit/0.5.11/lib.typ | 1148 +++++++++ .../preview/phonokit/0.5.11/multi-tier.typ | 510 ++++ packages/preview/phonokit/0.5.11/ot.typ | 1263 ++++++++++ .../preview/phonokit/0.5.11/phonetics.typ | 1017 ++++++++ packages/preview/phonokit/0.5.11/prosody.typ | 2200 +++++++++++++++++ packages/preview/phonokit/0.5.11/sonority.typ | 242 ++ .../preview/phonokit/0.5.11/sound-shift.typ | 261 ++ packages/preview/phonokit/0.5.11/typst.toml | 23 + packages/preview/phonokit/0.5.11/ui-lang.typ | 369 +++ packages/preview/phonokit/0.5.11/vowels.typ | 546 ++++ 51 files changed, 14903 insertions(+) create mode 100644 packages/preview/phonokit/0.5.11/LICENSE create mode 100644 packages/preview/phonokit/0.5.11/README.md create mode 100644 packages/preview/phonokit/0.5.11/_config.typ create mode 100644 packages/preview/phonokit/0.5.11/autosegmental.typ create mode 100644 packages/preview/phonokit/0.5.11/consonants.typ create mode 100644 packages/preview/phonokit/0.5.11/ex.typ create mode 100644 packages/preview/phonokit/0.5.11/extras.typ create mode 100644 packages/preview/phonokit/0.5.11/features.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/autoseg_example_1.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/autoseg_example_1.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/autoseg_example_2.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/autoseg_example_2.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/autoseg_example_3.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/autoseg_example_3.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/consonants_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/consonants_example.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/feat_geom.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/feat_geom.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/features_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/features_example.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/grid_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/grid_example.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/ipa_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/ipa_example.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/maxent_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/maxent_example.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/multi-tier_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/multi-tier_example.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/ot_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/ot_example.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/syllable_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/syllable_example.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/vowels_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/vowels_example.typ create mode 100644 packages/preview/phonokit/0.5.11/gallery/word_example.png create mode 100644 packages/preview/phonokit/0.5.11/gallery/word_example.typ create mode 100644 packages/preview/phonokit/0.5.11/geom.typ create mode 100644 packages/preview/phonokit/0.5.11/grids.typ create mode 100644 packages/preview/phonokit/0.5.11/hasse.typ create mode 100644 packages/preview/phonokit/0.5.11/intonational.typ create mode 100644 packages/preview/phonokit/0.5.11/ipa.typ create mode 100644 packages/preview/phonokit/0.5.11/lib.typ create mode 100644 packages/preview/phonokit/0.5.11/multi-tier.typ create mode 100644 packages/preview/phonokit/0.5.11/ot.typ create mode 100644 packages/preview/phonokit/0.5.11/phonetics.typ create mode 100644 packages/preview/phonokit/0.5.11/prosody.typ create mode 100644 packages/preview/phonokit/0.5.11/sonority.typ create mode 100644 packages/preview/phonokit/0.5.11/sound-shift.typ create mode 100644 packages/preview/phonokit/0.5.11/typst.toml create mode 100644 packages/preview/phonokit/0.5.11/ui-lang.typ create mode 100644 packages/preview/phonokit/0.5.11/vowels.typ diff --git a/packages/preview/phonokit/0.5.11/LICENSE b/packages/preview/phonokit/0.5.11/LICENSE new file mode 100644 index 0000000000..e116f4b73a --- /dev/null +++ b/packages/preview/phonokit/0.5.11/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Guilherme D. Garcia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/preview/phonokit/0.5.11/README.md b/packages/preview/phonokit/0.5.11/README.md new file mode 100644 index 0000000000..392d070081 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/README.md @@ -0,0 +1,176 @@ +
+ + + + phonokit logo + +
+ +
+ +[![Typst Package](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fguilhermegarcia%2Fphonokit%2Fmain%2Ftypst.toml&query=%24.package.version&prefix=v&logo=typst&label=package&color=239DAD)](https://typst.app/universe/package/phonokit) +[![MIT License](https://img.shields.io/badge/license-MIT-blue)](LICENSE) +[![User Manual](https://img.shields.io/badge/manual-.pdf-purple)](https://doi.org/10.5281/zenodo.18260076) + +
+ +**Charis font is needed** for this package to work exactly as intended, but you can also use your own font with `#phonokit-init(font: "...")`. New Computer Modern is used for arrows. + +## 📝 Some examples + + + + + + + + + + + + + + + + + + + + + + +
+ IPA transcription example +
IPA transcription based on tipa +
+ Consonant inventory table +
Consonant inventories (with pre-defined languages) +
+ Vowel trapezoid chart +
Vowel trapezoids (with pre-defined languages and arrows) +
+ Multi-tier phonological representation +
Multi-tier representations +
+ Syllable structure tree +
Syllable structure (onset-rhyme and moraic) +
+ Prosodic word tree +
Prosodic word (with metrical parsing) +
+ Metrical grid +
Metrical grids with IPA support +
+ Autosegmental feature spreading +
Autosegmental phonology: features +
+ Autosegmental tone representation +
Autosegmental phonology: tones +
+ Feature geometry tree +
Feature geometry +
+ OT tableau with shading +
OT tableaux with automatic shading +
+ MaxEnt tableau with probability bars +
MaxEnt tableaux with automatic calculation +
+ +Click on any image to view its source code. + +## 🔍 Manual + +See [**manual**](https://doi.org/10.5281/zenodo.18260076) for a comprehensive demonstration of available functions and their usage. + +### IPA Module + +- **tipa-style input**: Use familiar LaTeX tipa notation instead of hunting for Unicode symbols +- **Comprehensive symbol support**: most common IPA consonants, vowels, and diacritics +- **Vowel charts**: Plot vowels on the IPA vowel trapezoid with accurate positioning +- **Consonant tables**: Display consonants in the pulmonic IPA consonant table +- **Scalable charts**: Adjust size to fit your document layout (scaling includes text as expected) + +### Phonetics Module + +- **Sound shifts**: Create schematic chain shifts, mergers, and splits by placing symbols freely in two-dimensional space and connecting them with arrows +- **Voice Onset Time**: Generate pedagogical VOT timelines showing closure, release, aspiration, and voicing onset, with support for segment labels and localized interface labels +- **Vowel dispersion**: Plot illustrative F1/F2 vowel distributions with optional jitter, ellipses, and support for reading real vowel data from `csv` files + +### Prosody Module + +- **Prosodic structure visualization**: Draw syllable structures (onset-rhyme and moraic representations) as well as feet and prosodic words with simple and intuitive syntax. You can also define which symbols you prefer to use for different prosodic domains +- **Multi-tier representations**: Create complex non-linear representations +- **Metrical grids**: Inputs as strings or tuples +- **Sonority profile**: Visualize the sonority of a string +- **ToBI labels**: Add intonational labels to running text with `#int()`, with optional vertical lines for association + +### Autosegmental Module + +- **Features and tones**: Create autosegmental representation for both features and tones +- **Support for common processes**: Easily add linking, delinking, floating tones, one-to-many relationships and highlighting. Additional options for spacing and annotation also available + +### Feature Geometry Module + +- **Create complex feature geometry representations**: Pre-defined phonemes are available for quick representations +- **Intuitive arguments based on typical nodes**: Create custom representations with intuitive argument structure +- **Support for arrows and delinking**: Represent processes with arrows, which may or may not curve. Obstacle avoidance is attempted when arrows curve, but you can also customize arrow path. +- **Highlight nodes**: Easily highlight nodes with the `highlight` argument, which dims the whole representation *except* the nodes you need highlighted. + +### SPE module + +- **Feature matrices**: Create SPE-style feature matrices with `#feat()` and segment-based distinctive-feature matrices with `#feat-matrix()` + +### Optimality Theory Module + +- **OT tableaux**: Create publication-ready Optimality Theory tableaux with automatic formatting +- **Automatic shading**: Cells are automatically grayed out after fatal violations +- **Winner indication**: Optimal candidates automatically marked with ☞ (pointing finger) +- **IPA support**: Input and candidate forms can use tipa-style IPA notation +- **Hasse diagrams**: Generate Hasse diagrams to visualize constraint rankings + +### Maximum Entropy Module + +- **MaxEnt tableaux**: Generate Maximum Entropy grammar tableaux with probability calculations +- **Automatic calculations**: Computes harmony scores H(x), unnormalized probabilities P*(x), and normalized probabilities P(x) +- **Visual probability bars**: Optional graphical representation of candidate probabilities +- **IPA support**: Input and candidate forms can use tipa-style IPA notation + +### Harmonic Grammar Module + +- **HG tableaux**: Generate HG tableaux with automatic calculation of harmony given constraint weights and violations +- **Noisy HG**: Generate NHG tableaux with automatic calculation of harmony and probabilities derived from simulated noise and multiple evaluations + +### General Module + +- **Numbered examples**: Create examples and sub-examples with labels and correct alignment +- **Shortcuts**: Quick commands to add a range of arrows, angle brackets for extrametricality, and SPE-style underlines for context + +*** + +## 📦 Package Repository + +- `https://github.com/guilhermegarcia/phonokit` [(most up-to-date version)](http://github.com/guilhermegarcia/phonokit) +- `https://typst.app/universe/package/phonokit` [(published on Typst)](https://typst.app/universe/package/phonokit) + +## Package website + +For the most up-to-date information about the package, vignettes and demos, visit . + +## License + +MIT + +## Author + +**Guilherme D. Garcia** \ +Email: \ +Website: + +## 📚 Citation + +If you use this package in your research, please visit its GitHub repository and cite it using the metadata from the `CITATION.cff` file or click the "Cite this repository" button in the GitHub sidebar. + +## Contributing + +Contributions are welcome! Please feel free to submit issues or pull requests. diff --git a/packages/preview/phonokit/0.5.11/_config.typ b/packages/preview/phonokit/0.5.11/_config.typ new file mode 100644 index 0000000000..6ccb1944da --- /dev/null +++ b/packages/preview/phonokit/0.5.11/_config.typ @@ -0,0 +1,8 @@ +// Shared configuration state for phonokit +// This module provides a global font setting that all modules can access. + +#let phonokit-font = state("phonokit-font", "Charis") + +#let phonokit-init(font: "Charis") = { + phonokit-font.update(font) +} diff --git a/packages/preview/phonokit/0.5.11/autosegmental.typ b/packages/preview/phonokit/0.5.11/autosegmental.typ new file mode 100644 index 0000000000..275f933d0e --- /dev/null +++ b/packages/preview/phonokit/0.5.11/autosegmental.typ @@ -0,0 +1,275 @@ +#import "@preview/cetz:0.5.2" +#import "ipa.typ": ipa +#import "_config.typ": phonokit-font + +#let autoseg( + segments, + features: (), + links: (), + delinks: (), + spacing: 1.5, + arrow: false, + tone: false, + highlight: (), + float: (), + multilinks: (), + baseline: 40%, + gloss: "", + dash: "dashed", +) = { + box(inset: 1.2em, baseline: baseline, cetz.canvas({ + import cetz.draw: * + + // Coordinate positions depend on whether we're drawing tones or features + let seg_y = if tone { 0 } else { 0.8 } + let feat_y = if tone { 0.8 } else { 0 } + let gloss_y = if tone { -0.8 } else { 1.6 } + let seg_anchor_dir = if tone { "north" } else { "south" } + let feat_anchor_dir = if tone { "south" } else { "north" } + let gloss_anchor_dir = if tone { "north" } else { "south" } + + for (i, seg) in segments.enumerate() { + let feat = features.at(i, default: "") + let x = i * spacing + + // Check if this position is part of a multilink (skip normal drawing if so) + let is_multilinked = multilinks.any(entry => { + let tone_spec = entry.at(0) + let tone_pos = if type(tone_spec) == array { tone_spec.at(0) } else { tone_spec } + tone_pos == i + }) + + // Handle multiple tones/features as an array (for branching) + let feat_array = if type(feat) == array { feat } else { (feat,) } + let num_feats = feat_array.len() + + group(name: "n" + str(i), { + // 3. Labels (segment) + // Use horizon alignment to ensure consistent baseline across all segments + content((x, seg_y), padding: 0.1, anchor: seg_anchor_dir, box(height: 1em, align(horizon, context text( + font: phonokit-font.get(), + ipa(seg), + )))) + + // Process each tone/feature (creates branching for multiple tones) + // Skip if this position is multilinked (will be drawn by multilink code) + for (j, f) in feat_array.enumerate() { + if f != "" and not is_multilinked { + // Calculate horizontal offset for multiple tones + let x_offset = if num_feats > 1 { (j - (num_feats - 1) / 2) * 0.6 } else { 0 } + let feat_x = x + x_offset + + // 1. Draw the vertical stem if not floating + if i not in float { + line((feat_x, feat_y), (x, seg_y), stroke: 0.05em, name: "stem" + str(j)) + } + + // 2. Feature/tone label with optional circle highlight + // Check if this specific tone should be highlighted + let should_highlight = tone and (i in highlight or (i, j) in highlight) + let box_stroke = if should_highlight { 0.05em + black } else { none } + content((feat_x, feat_y), padding: 0.1, anchor: feat_anchor_dir, box( + stroke: box_stroke, + inset: 0.15em, + radius: 100%, + width: 1.2em, + height: 1.2em, + align(center + horizon, context text(font: phonokit-font.get(), f)), + )) + + // Create anchor for this specific tone (for sub-indexing in links) + anchor("feat" + str(j), (feat_x, feat_y)) + } + } + + // 3. Anchors for association lines (center position and segment) + anchor("seg", (x, seg_y)) + anchor("feat", (x, feat_y)) + }) + } + + // 4. Draw the spreading links + for (src, dest) in links { + // Parse src and dest - can be int or (pos, sub) tuple for targeting specific tones + let parse_index = idx => { + if type(idx) == array { + (pos: idx.at(0), sub: idx.at(1)) + } else { + (pos: idx, sub: none) + } + } + + let src_parsed = parse_index(src) + let dest_parsed = parse_index(dest) + + // Build anchor names with optional sub-index + let get_anchor = (parsed, tier) => { + let base = "n" + str(parsed.pos) + if tier == "feat" and parsed.sub != none { + base + ".feat" + str(parsed.sub) + } else if tier == "feat" { + base + ".feat" + } else { + base + ".seg" + } + } + + // Both modes: link from feature/tone[src] to segment[dest] + let start_anchor = get_anchor(src_parsed, "feat") + let end_anchor = get_anchor(dest_parsed, "seg") + + // Draw the association line + let link-stroke = if dash == "solid" { (thickness: 0.05em) } else { (dash: dash, thickness: 0.05em) } + line(start_anchor, end_anchor, stroke: link-stroke) + + if arrow { + // Arrow points from feature/tone toward segment + line(start_anchor, end_anchor, stroke: (thickness: 0em), mark: (end: "stealth"), fill: black) + } + } + + // 5. Draw multi-linked tones (shared tones positioned between segments) + for entry in multilinks { + let (tone_spec, seg_positions) = entry + + // Parse tone specification (can be index or (pos, sub)) + let tone_parsed = if type(tone_spec) == array { + (pos: tone_spec.at(0), sub: tone_spec.at(1)) + } else { + (pos: tone_spec, sub: none) + } + + // Calculate midpoint position for the tone + let min_seg = calc.min(..seg_positions) + let max_seg = calc.max(..seg_positions) + let mid_x = ((min_seg + max_seg) / 2) * spacing + + // Get the tone text + let tone_feat = features.at(tone_parsed.pos, default: "") + let tone_array = if type(tone_feat) == array { tone_feat } else { (tone_feat,) } + let tone_text = if tone_parsed.sub != none { + tone_array.at(tone_parsed.sub) + } else { + if type(tone_feat) == array { tone_feat.join(" ") } else { tone_feat } + } + + // Draw the tone at midpoint + let box_stroke = if tone and (tone_parsed.pos in highlight or (tone_parsed.pos, tone_parsed.sub) in highlight) { + 0.05em + black + } else { + none + } + content((mid_x, feat_y), padding: 0.1, anchor: feat_anchor_dir, box( + stroke: box_stroke, + inset: 0.15em, + radius: 100%, + width: 1.2em, + height: 1.2em, + align(center + horizon, context text(font: phonokit-font.get(), tone_text)), + )) + + // Draw solid lines to each segment (no arrows, no dashes) + for seg_pos in seg_positions { + let seg_x = seg_pos * spacing + line((mid_x, feat_y), (seg_x, seg_y), stroke: 0.05em) + } + } + + // 6. Draw delinks on association lines (drawn after multilinks to be on top) + for (src, dest) in delinks { + // Parse src and dest - can be int or (pos, sub) tuple + let parse_index = idx => { + if type(idx) == array { + (pos: idx.at(0), sub: idx.at(1)) + } else { + (pos: idx, sub: none) + } + } + + let src_parsed = parse_index(src) + let dest_parsed = parse_index(dest) + + // Calculate dest position + let dest_x = dest_parsed.pos * spacing + + // Check if source is multilinked + let is_multilinked = multilinks.any(entry => { + let tone_spec = entry.at(0) + let tone_pos = if type(tone_spec) == array { tone_spec.at(0) } else { tone_spec } + tone_pos == src_parsed.pos + }) + + let src_feat_x = if is_multilinked { + // Find the multilink entry for this source + let multilink_entry = multilinks.find(entry => { + let tone_spec = entry.at(0) + let tone_pos = if type(tone_spec) == array { tone_spec.at(0) } else { tone_spec } + tone_pos == src_parsed.pos + }) + let seg_positions = multilink_entry.at(1) + let min_seg = calc.min(..seg_positions) + let max_seg = calc.max(..seg_positions) + ((min_seg + max_seg) / 2) * spacing + } else { + // Regular tone or branching tone + let src_x = src_parsed.pos * spacing + let src_feat = features.at(src_parsed.pos, default: "") + let src_feat_array = if type(src_feat) == array { src_feat } else { (src_feat,) } + let num_src_feats = src_feat_array.len() + + let src_x_offset = if num_src_feats > 1 and src_parsed.sub != none { + (src_parsed.sub - (num_src_feats - 1) / 2) * 0.6 + } else { + 0 + } + src_x + src_x_offset + } + + // Calculate midpoint for delink marks + let mid_x = (src_feat_x + dest_x) / 2 + let mid_y = (seg_y + feat_y) / 2 + + // Draw the delink marks (two parallel lines perpendicular to the association line) + // Calculate the direction vector and its perpendicular + let dx = dest_x - src_feat_x + let dy = seg_y - feat_y + let length = calc.sqrt(dx * dx + dy * dy) + + // Normalized direction + let dir_x = dx / length + let dir_y = dy / length + + // Perpendicular direction (rotate 90 degrees) + let perp_x = -dir_y + let perp_y = dir_x + + let offset = 0.15 + let spacing_offset = 0.06 + + // First delink line (closer to tone) + let p1_start = ( + mid_x - offset * perp_x - spacing_offset * dir_x, + mid_y - offset * perp_y - spacing_offset * dir_y, + ) + let p1_end = (mid_x + offset * perp_x - spacing_offset * dir_x, mid_y + offset * perp_y - spacing_offset * dir_y) + + // Second delink line (closer to segment) + let p2_start = ( + mid_x - offset * perp_x + spacing_offset * dir_x, + mid_y - offset * perp_y + spacing_offset * dir_y, + ) + let p2_end = (mid_x + offset * perp_x + spacing_offset * dir_x, mid_y + offset * perp_y + spacing_offset * dir_y) + + line(p1_start, p1_end, stroke: 0.05em) + line(p2_start, p2_end, stroke: 0.05em) + } + + // 7. Draw gloss (if provided) + if gloss != "" and gloss != () { + // Calculate center of the entire representation + let center_x = ((segments.len() - 1) / 2) * spacing + // Use fixed-height box to ensure consistent baseline alignment across autoseg instances + content((center_x, gloss_y), padding: 0.1, anchor: gloss_anchor_dir, box(height: 1em, align(horizon, context text(size: 0.9em, font: phonokit-font.get(), gloss)))) + } + })) +} diff --git a/packages/preview/phonokit/0.5.11/consonants.typ b/packages/preview/phonokit/0.5.11/consonants.typ new file mode 100644 index 0000000000..946d5f967f --- /dev/null +++ b/packages/preview/phonokit/0.5.11/consonants.typ @@ -0,0 +1,938 @@ +#import "@preview/cetz:0.5.2": canvas, draw +#import "ipa.typ": ipa-to-unicode +#import "_config.typ": phonokit-font +#import "ui-lang.typ": resolve-ui-lang, ui-lang-error, ui-consonant-labels + +// Consonant data with place, manner, and voicing +// place: 0=bilabial, 1=labiodental, 2=dental, 3=alveolar, 4=postalveolar +// 5=retroflex, 6=palatal, 7=velar, 8=uvular, 9=pharyngeal, 10=glottal +// manner: 0=plosive, 1=nasal, 2=trill, 3=tap/flap, 4=fricative, +// 5=lateral fricative, 6=approximant, 7=lateral approximant +#let consonant-data = ( + // Plosives (row 0) + "p": (place: 0, manner: 0, voicing: false), + "b": (place: 0, manner: 0, voicing: true), + "t": (place: 3, manner: 0, voicing: false), + "d": (place: 3, manner: 0, voicing: true), + "ʈ": (place: 5, manner: 0, voicing: false), + "ɖ": (place: 5, manner: 0, voicing: true), + "c": (place: 6, manner: 0, voicing: false), + "ɟ": (place: 6, manner: 0, voicing: true), + "k": (place: 7, manner: 0, voicing: false), + "ɡ": (place: 7, manner: 0, voicing: true), + "g": (place: 7, manner: 0, voicing: true), + "q": (place: 8, manner: 0, voicing: false), + "ɢ": (place: 8, manner: 0, voicing: true), + "ʔ": (place: 10, manner: 0, voicing: false), + // Nasals (row 1) + "m": (place: 0, manner: 1, voicing: true), + "ɱ": (place: 1, manner: 1, voicing: true), + "n": (place: 3, manner: 1, voicing: true), + "ɳ": (place: 5, manner: 1, voicing: true), + "ɲ": (place: 6, manner: 1, voicing: true), + "ŋ": (place: 7, manner: 1, voicing: true), + "ɴ": (place: 8, manner: 1, voicing: true), + // Trills (row 2) + "ʙ": (place: 0, manner: 2, voicing: true), + "r": (place: 3, manner: 2, voicing: true), + "ʀ": (place: 8, manner: 2, voicing: true), + // Tap or Flap (row 3) + "ⱱ": (place: 1, manner: 3, voicing: true), + "ɾ": (place: 3, manner: 3, voicing: true), + "ɽ": (place: 5, manner: 3, voicing: true), + // Fricatives (row 4) + "ɸ": (place: 0, manner: 4, voicing: false), + "β": (place: 0, manner: 4, voicing: true), + "f": (place: 1, manner: 4, voicing: false), + "v": (place: 1, manner: 4, voicing: true), + "θ": (place: 2, manner: 4, voicing: false), + "ð": (place: 2, manner: 4, voicing: true), + "s": (place: 3, manner: 4, voicing: false), + "z": (place: 3, manner: 4, voicing: true), + "ʃ": (place: 4, manner: 4, voicing: false), + "ʒ": (place: 4, manner: 4, voicing: true), + "ʂ": (place: 5, manner: 4, voicing: false), + "ʐ": (place: 5, manner: 4, voicing: true), + "ç": (place: 6, manner: 4, voicing: false), + "ʝ": (place: 6, manner: 4, voicing: true), + "x": (place: 7, manner: 4, voicing: false), + "ɣ": (place: 7, manner: 4, voicing: true), + "χ": (place: 8, manner: 4, voicing: false), + "ʁ": (place: 8, manner: 4, voicing: true), + "ħ": (place: 9, manner: 4, voicing: false), + "ʕ": (place: 9, manner: 4, voicing: true), + "h": (place: 10, manner: 4, voicing: false), + "ɦ": (place: 10, manner: 4, voicing: true), + // Lateral fricatives (row 5) + "ɬ": (place: 3, manner: 5, voicing: false), + "ɮ": (place: 3, manner: 5, voicing: true), + // Approximants (row 6) + "w": (place: 0, manner: 6, voicing: true), // labiovelar, shown under bilabial + "ʋ": (place: 1, manner: 6, voicing: true), + "ɹ": (place: 3, manner: 6, voicing: true), + "ɻ": (place: 5, manner: 6, voicing: true), + "j": (place: 6, manner: 6, voicing: true), + "ɰ": (place: 7, manner: 6, voicing: true), + // Lateral approximants (row 7) + "l": (place: 3, manner: 7, voicing: true), + "ɭ": (place: 5, manner: 7, voicing: true), + "ʎ": (place: 6, manner: 7, voicing: true), + "ʟ": (place: 7, manner: 7, voicing: true), +) + +// Aspirated plosive data (shown in separate row when aspirated: true) +#let aspirated-plosive-data = ( + // Aspirated plosives (voiceless only - aspiration is contrastive with plain voiceless) + "pʰ": (place: 0, voicing: false), + "tʰ": (place: 3, voicing: false), + "ʈʰ": (place: 5, voicing: false), + "cʰ": (place: 6, voicing: false), + "kʰ": (place: 7, voicing: false), + "qʰ": (place: 8, voicing: false), +) + +// Affricate data (shown in separate row when affricates: true) +// These appear after fricatives in the chart +// Note: Displayed without tie bars since the row label makes it clear they're affricates +#let affricate-data = ( + // Labiodental affricates + "pf": (place: 1, voicing: false), + "bv": (place: 1, voicing: true), + // Alveolar affricates + "ts": (place: 3, voicing: false), + "dz": (place: 3, voicing: true), + // Postalveolar affricates + "tʃ": (place: 4, voicing: false), + "dʒ": (place: 4, voicing: true), + // Retroflex affricates + "ʈʂ": (place: 5, voicing: false), + "ɖʐ": (place: 5, voicing: true), + // Alveolo-palatal affricates + "tɕ": (place: 4, voicing: false), // Use postalveolar column + "dʑ": (place: 4, voicing: true), +) + +// Aspirated affricate data (shown when both affricates: true AND aspirated: true) +#let aspirated-affricate-data = ( + // Aspirated affricates (voiceless only) + "tsʰ": (place: 3, voicing: false), + "tʃʰ": (place: 4, voicing: false), + "ʈʂʰ": (place: 5, voicing: false), + "tɕʰ": (place: 4, voicing: false), // Alveolo-palatal +) + +// Column labels (places of articulation) +#let places = ( + "Bilabial", + "Labiodental", + "Dental", + "Alveolar", + "Postalveolar", + "Retroflex", + "Palatal", + "Velar", + "Uvular", + "Pharyngeal", + "Glottal", +) + +#let places-short = ( + "Bilab", + "Labdent", + "Dent", + "Alv", + "Postalv", + "Retro", + "Pal", + "Vel", + "Uvu", + "Phar", + "Glot", +) + +// Row labels (manners of articulation) +#let manners = ( + "Plosive", + "Nasal", + "Trill", + "Tap or Flap", + "Fricative", + "Lateral fricative", + "Approximant", + "Lateral approximant", +) + +#let manners-short = ( + "Plos", + "Nas", + "Trill", + "Tap/Flap", + "Fric", + "Lat fric", + "Approx", + "Lat approx", +) + +// Language consonant inventories +#let language-consonants = ( + "all": "pbtdʈɖcɟkɡqɢʔmɱnɳɲŋɴʙrʀⱱɾɽɸβfvθðszʃʒʂʐçʝxɣχʁħʕhɦɬɮʋɹɻjɰwlɭʎʟ", + "english": "pbmnŋtdkɡfvθðszʃʒhlɹwj", + "spanish": "pbmnɲtdkɡfθsxlrɾj", + "french": "pbmnɲtdkɡfrvszʃʒljw", + "german": "pbmntdkɡfvszʃʒçxhʁlj", + "italian": "pbmnɲtdkɡfvszʃʎlrj", + "japanese": "pbmnɲtdkɡçɸsʃzʒhɾj", + "portuguese": "pbmnɲtdkɡfvszʃʒʎxlɾjw", + "russian": "pbmntdkɡfvszʃʒxlrj", + "arabic": "btdkqʔmnfvðszʃxɣħʕhlrj", +) + +// Language affricate inventories (used when affricates: true) +// Note: No tie bars needed - the "Affricate" row label makes it clear +// Note: Only one affricate pair per place - "all" uses more common variants +#let language-affricates = ( + "all": "pfbvtsdztʃdʒʈʂɖʐ", // tʃ/dʒ chosen over tɕ/dʑ (more common) + "english": "tʃdʒ", + "spanish": "tʃ", + "french": "", // No native affricates + "german": "pfts", + "italian": "tsdztʃdʒ", + "japanese": "", // Do not include allophones + "portuguese": "tʃdʒ", + "russian": "tstʃ", + "arabic": "", // No native affricates +) + +// Language aspirated plosive inventories (used when aspirated: true) +#let language-aspirated-plosives = ( + "all": "pʰtʰʈʰcʰkʰqʰ", + "english": "", // English aspiration is allophonic, not phonemic + "spanish": "", + "french": "", + "german": "", + "italian": "", + "japanese": "", + "portuguese": "", + "russian": "", + "arabic": "", +) + +// Language aspirated affricate inventories (used when both affricates: true AND aspirated: true) +#let language-aspirated-affricates = ( + "all": "tsʰtʃʰʈʂʰtɕʰ", + "english": "", + "spanish": "", + "french": "", + "german": "", + "italian": "", + "japanese": "", + "portuguese": "", + "russian": "", + "arabic": "", +) + +// Helper function to extract braced content {phoneme} from input +// Braced content can be affricates, aspirated consonants, etc. +#let extract-braced-content(input) = { + let braced-items = () + let cleaned = "" + let in-braces = false + let current-item = "" + + for char in input.clusters() { + if char == "{" { + in-braces = true + current-item = "" + } else if char == "}" { + if in-braces and current-item != "" { + braced-items.push(current-item) + } + in-braces = false + current-item = "" + } else if in-braces { + current-item += char + } else { + cleaned += char + } + } + + (braced-items: braced-items, cleaned: cleaned) +} + +// Helper function to categorize braced items into affricates, aspirated plosives, and aspirated affricates +#let categorize-braced-items(braced-items) = { + let affricates = () + let aspirated-plosives = () + let aspirated-affricates = () + + for item in braced-items { + // Convert IPA notation to Unicode (spaces are required for diacritics like \h) + let converted = ipa-to-unicode(item) + + // Check which category this belongs to + if converted in aspirated-affricate-data { + aspirated-affricates.push(converted) + } else if converted in aspirated-plosive-data { + aspirated-plosives.push(converted) + } else if converted in affricate-data { + affricates.push(converted) + } + // Unknown braced items are silently ignored + } + + ( + affricates: affricates, + aspirated-plosives: aspirated-plosives, + aspirated-affricates: aspirated-affricates, + ) +} + +// Main consonants function +#let consonants( + ..args, // Optional positional consonant-string (read below) — allows `lang`-only calls + lang: none, + ui-lang: "en", + affricates: false, + aspirated: false, + abbreviate: false, + simplify: false, + delete-cols: (), + delete-rows: (), + cell-width: 1.8, + cell-height: 0.9, + label-width: 3.5, + label-height: 1.2, + scale: 0.7, +) = { + // Read the optional positional argument: consonant symbols (tipa-style IPA, + // optionally with braced affricates/aspirated items) or a built-in language + // name. Optional (via the `..args` sink) so that `lang`-only calls like + // `consonants(lang: "spanish")` work — a parameter with a default value would + // be named-only in Typst and could not be passed positionally. + assert(args.pos().len() <= 1, + message: "consonants: expected at most one positional argument (the consonant string)") + assert(args.named().len() == 0, + message: "consonants: unexpected named argument(s): " + args.named().keys().join(", ")) + let consonant-string = args.pos().at(0, default: none) + + // Determine which consonants to plot + let consonants-to-plot = "" + let custom-affricates-string = "" + let custom-aspirated-plosives-string = "" + let custom-aspirated-affricates-string = "" + let error-msg = none + let ui-locale = resolve-ui-lang(ui-lang) + + if ui-locale == none { + return ui-lang-error(ui-lang) + } + let labels = ui-consonant-labels(ui-locale) + + // Check if consonant-string is actually a language name + if consonant-string != none and consonant-string in language-consonants { + consonants-to-plot = language-consonants.at(consonant-string) + } else if lang != none { + if lang in language-consonants { + consonants-to-plot = language-consonants.at(lang) + } else { + let available = language-consonants.keys().join(", ") + error-msg = [*Error:* Language "#lang" not available. \ Available languages: #available] + } + } else if consonant-string != none and consonant-string != "" { + // Use as manual consonant specification + // Extract braced content first (affricates, aspirated consonants, etc.) + let extracted = extract-braced-content(consonant-string) + + // Convert IPA notation to Unicode for consonants (excluding braced items) + consonants-to-plot = ipa-to-unicode(extracted.cleaned) + + // Categorize braced items into their respective types + let categorized = categorize-braced-items(extracted.braced-items) + // Note: .join("") returns none for empty arrays in Typst, so we check length first + if categorized.affricates.len() > 0 { + custom-affricates-string = categorized.affricates.join("") + } + if categorized.aspirated-plosives.len() > 0 { + custom-aspirated-plosives-string = categorized.aspirated-plosives.join("") + } + if categorized.aspirated-affricates.len() > 0 { + custom-aspirated-affricates-string = categorized.aspirated-affricates.join("") + } + } else { + error-msg = [*Error:* Either provide consonant string or language name] + } + + // If there's an error, display it and return + if error-msg != none { + return error-msg + } + + // Determine which affricates to plot (if affricates: true) + let affricates-to-plot = "" + if affricates { + // Check if we used a language name + if consonant-string != none and consonant-string in language-affricates { + affricates-to-plot = language-affricates.at(consonant-string) + } else if lang != none and lang in language-affricates { + affricates-to-plot = language-affricates.at(lang) + } else { + // For custom input, use only the extracted affricates from braces + affricates-to-plot = custom-affricates-string + } + } + + // Determine which aspirated consonants to plot (if aspirated: true) + let aspirated-plosives-to-plot = "" + let aspirated-affricates-to-plot = "" + if aspirated { + // Aspirated plosives + if consonant-string != none and consonant-string in language-aspirated-plosives { + aspirated-plosives-to-plot = language-aspirated-plosives.at(consonant-string) + } else if lang != none and lang in language-aspirated-plosives { + aspirated-plosives-to-plot = language-aspirated-plosives.at(lang) + } else { + // For custom input, use aspirated plosives extracted from braces + aspirated-plosives-to-plot = custom-aspirated-plosives-string + } + + // Aspirated affricates (only if affricates is also true) + if affricates { + if consonant-string != none and consonant-string in language-aspirated-affricates { + aspirated-affricates-to-plot = language-aspirated-affricates.at(consonant-string) + } else if lang != none and lang in language-aspirated-affricates { + aspirated-affricates-to-plot = language-aspirated-affricates.at(lang) + } else { + // For custom input, use aspirated affricates extracted from braces + aspirated-affricates-to-plot = custom-aspirated-affricates-string + } + } + } + + // If simplify is true, auto-delete empty columns and rows + if simplify { + // Collect all used places and manners from the data to plot + let used-places = () + let used-manners = () + + // Scan main consonants + for consonant in consonants-to-plot.clusters() { + if consonant in consonant-data { + let info = consonant-data.at(consonant) + if info.place not in used-places { used-places.push(info.place) } + if info.manner not in used-manners { used-manners.push(info.manner) } + } + } + + // Special /w/ handling: also occupies velar column if /ɰ/ is absent + if consonants-to-plot.contains("w") and not consonants-to-plot.contains("ɰ") { + if 7 not in used-places { used-places.push(7) } + } + + // Scan aspirated plosives + if aspirated { + for asp-plosive in aspirated-plosive-data.keys() { + if aspirated-plosives-to-plot.contains(asp-plosive) { + let info = aspirated-plosive-data.at(asp-plosive) + if info.place not in used-places { used-places.push(info.place) } + // Aspirated plosives share manner 0 (Plosive row) + if 0 not in used-manners { used-manners.push(0) } + } + } + } + + // Scan affricates + if affricates { + let affricates-cleaned = affricates-to-plot.replace("͡", "") + for affricate in affricate-data.keys() { + if affricates-cleaned.contains(affricate) { + let info = affricate-data.at(affricate) + if info.place not in used-places { used-places.push(info.place) } + // Affricates have their own inserted row, no base manner to add + } + } + } + + // Scan aspirated affricates + if aspirated and affricates { + for asp-affricate in aspirated-affricate-data.keys() { + if aspirated-affricates-to-plot.contains(asp-affricate) { + let info = aspirated-affricate-data.at(asp-affricate) + if info.place not in used-places { used-places.push(info.place) } + } + } + } + + // Merge: add unused places/manners to delete lists + for i in range(places.len()) { + if i not in used-places and i not in delete-cols { + delete-cols.push(i) + } + } + for i in range(manners.len()) { + if i not in used-manners and i not in delete-rows { + delete-rows.push(i) + } + } + } + + // Select label sets based on abbreviation setting + let place-labels = if abbreviate { labels.places_short } else { labels.places } + let manner-labels = if abbreviate { labels.manners_short } else { labels.manners } + + // Filter deleted columns and build column remapping + let kept-cols = range(places.len()).filter(i => i not in delete-cols) + let display-places = kept-cols.map(i => place-labels.at(i)) + let col-remap = (:) + for (new-i, orig-i) in kept-cols.enumerate() { + col-remap.insert(str(orig-i), new-i) + } + + // Filter deleted rows and build display-manners with optional row insertions + let kept-base-rows = range(manners.len()).filter(i => i not in delete-rows) + let display-manners = () + let manner-to-row = (:) + let aspirated-plosive-row = -1 + let affricate-row = -1 + let aspirated-affricate-row = -1 + let current-row = 0 + + for base-row in kept-base-rows { + display-manners.push(manner-labels.at(base-row)) + manner-to-row.insert(str(base-row), current-row) + current-row += 1 + + // Insert aspirated plosive row after Plosive (base 0) + if base-row == 0 and aspirated { + let asp-label = if abbreviate { labels.aspirated_plosive_short } else { labels.aspirated_plosive } + display-manners.push(asp-label) + aspirated-plosive-row = current-row + current-row += 1 + } + + // Insert affricate rows after Fricative (base 4) + if base-row == 4 and affricates { + let affr-label = if abbreviate { labels.affricate_short } else { labels.affricate } + display-manners.push(affr-label) + affricate-row = current-row + current-row += 1 + + if aspirated { + let asp-affr-label = if abbreviate { labels.aspirated_affricate_short } else { labels.aspirated_affricate } + display-manners.push(asp-affr-label) + aspirated-affricate-row = current-row + current-row += 1 + } + } + } + + // Calculate scaled dimensions + let scaled-cell-width = cell-width * scale + let scaled-cell-height = cell-height * scale + let scaled-label-width = label-width * scale + let scaled-label-height = label-height * scale + let scaled-font-size = 18 * scale + let scaled-label-font-size = 9 * scale + let scaled-circle-radius = 0.3 * scale + let scaled-line-thickness = 0.8 * scale + + let num-cols = display-places.len() + let num-rows = display-manners.len() + + context { + let label-padding = 0.35 * scale + let column-widths = display-places.map(place => calc.max( + scaled-cell-width, + measure(text(size: scaled-label-font-size * 1pt, font: phonokit-font.get(), place)).width / 1cm + label-padding, + )) + let resolved-label-width = calc.max( + scaled-label-width, + calc.max(..display-manners.map(manner => measure(text(size: scaled-label-font-size * 1pt, font: phonokit-font.get(), manner)).width / 1cm)) + 0.35 * scale, + ) + + let col-starts = () + let x-cursor = resolved-label-width + for width in column-widths { + col-starts.push(x-cursor) + x-cursor += width + } + + canvas({ + import draw: * + + // Calculate total dimensions + let total-width = resolved-label-width + column-widths.sum() + let total-height = scaled-label-height + (num-rows * scaled-cell-height) + + // Draw column headers (places of articulation) + for (i, place) in display-places.enumerate() { + let col-width = column-widths.at(i) + let x = col-starts.at(i) + (col-width / 2) + let y = total-height / 2 - (scaled-label-height * 0.65) + + content((x, y), text(size: scaled-label-font-size * 1pt, font: phonokit-font.get(), top-edge: "cap-height", bottom-edge: "baseline", place), anchor: "center") + } + + // Draw row headers (manners of articulation) + for (i, manner) in display-manners.enumerate() { + let x = resolved-label-width - 0.2 + let y = total-height / 2 - scaled-label-height - (i * scaled-cell-height) - (scaled-cell-height / 2) + + content((x, y), text(size: scaled-label-font-size * 1pt, font: phonokit-font.get(), manner), anchor: "east") + } + + // Row indices for grid drawing (from the remapping built above) + let fricative-display-row = manner-to-row.at("4", default: -1) + + // Draw grid lines + // Vertical lines + for i in range(num-cols + 1) { + let x = if i == num-cols { total-width } else { col-starts.at(i) } + let y1 = total-height / 2 - scaled-label-height + let y2 = -total-height / 2 + + // Check if this vertical line is at a dental-alveolar or alveolar-postalveolar boundary + let is-special = false + if i > 0 and i < num-cols { + let left-orig = kept-cols.at(i - 1) + let right-orig = kept-cols.at(i) + if (left-orig == 2 and right-orig == 3) or (left-orig == 3 and right-orig == 4) { + is-special = true + } + } + + if is-special { + // Draw line segments only for fricative and affricate rows + for row in range(num-rows) { + if row == fricative-display-row or row == affricate-row or row == aspirated-affricate-row { + let row-y1 = total-height / 2 - scaled-label-height - (row * scaled-cell-height) + let row-y2 = row-y1 - scaled-cell-height + line((x, row-y1), (x, row-y2), stroke: (paint: gray.lighten(20%), thickness: scaled-line-thickness * 1pt)) + } + } + } else { + // Draw normal full-height vertical line for other columns + line((x, y1), (x, y2), stroke: (paint: gray.lighten(20%), thickness: scaled-line-thickness * 1pt)) + } + } + + // Horizontal lines + for i in range(num-rows + 1) { + let y = total-height / 2 - scaled-label-height - (i * scaled-cell-height) + let x1 = resolved-label-width + let x2 = total-width + line((x1, y), (x2, y), stroke: (paint: gray.lighten(20%), thickness: scaled-line-thickness * 1pt)) + } + + // Gray out impossible consonant cells + // Format: (display-row, orig-col, half) where half can be "full", "voiced", "voiceless" + // Row positions come from manner-to-row mapping + let plosive-row = manner-to-row.at("0", default: -1) + let nasal-row = manner-to-row.at("1", default: -1) + let trill-row = manner-to-row.at("2", default: -1) + let tap-row = manner-to-row.at("3", default: -1) + let lat-fric-row = manner-to-row.at("5", default: -1) + let approx-row = manner-to-row.at("6", default: -1) + let lat-approx-row = manner-to-row.at("7", default: -1) + + let impossible-cells = ( + // Lateral fricative + (lat-fric-row, 0, "full"), + (lat-fric-row, 1, "full"), + (lat-fric-row, 9, "full"), + (lat-fric-row, 10, "full"), + // Lateral approximant + (lat-approx-row, 0, "full"), + (lat-approx-row, 1, "full"), + (lat-approx-row, 9, "full"), + (lat-approx-row, 10, "full"), + // Trill + (trill-row, 7, "full"), + (trill-row, 10, "full"), + // Tap or flap + (tap-row, 7, "full"), + (tap-row, 10, "full"), + // Approximant + (approx-row, 10, "full"), + // Plosive - voiced side only + (plosive-row, 9, "voiced"), + (plosive-row, 10, "voiced"), + // Nasal + (nasal-row, 9, "full"), + (nasal-row, 10, "full"), + ) + + // Add aspirated plosive impossible cells if that row exists + if aspirated-plosive-row >= 0 { + impossible-cells = ( + impossible-cells + + ( + (aspirated-plosive-row, 9, "full"), + (aspirated-plosive-row, 10, "full"), + ) + ) + } + + for (row, orig-col, half) in impossible-cells { + if row < 0 or str(orig-col) not in col-remap { + // Row or column was deleted, skip + } else { + let col = col-remap.at(str(orig-col)) + let col-width = column-widths.at(col) + let cell-x = col-starts.at(col) + let cell-y = total-height / 2 - scaled-label-height - (row * scaled-cell-height) + + if half == "full" { + // Fill entire cell + rect( + (cell-x, cell-y), + (cell-x + col-width, cell-y - scaled-cell-height), + fill: gray.lighten(70%), + stroke: (paint: gray.lighten(20%), thickness: scaled-line-thickness * 1pt), + ) + } else if half == "voiced" { + // Fill right half of cell (voiced side) + let mid-x = cell-x + (col-width / 2) + rect( + (mid-x, cell-y), + (cell-x + col-width, cell-y - scaled-cell-height), + fill: gray.lighten(70%), + stroke: (paint: gray.lighten(20%), thickness: scaled-line-thickness * 1pt), + ) + } else if half == "voiceless" { + // Fill left half of cell (voiceless side) + let mid-x = cell-x + (col-width / 2) + rect( + (cell-x, cell-y), + (mid-x, cell-y - scaled-cell-height), + fill: gray.lighten(70%), + stroke: (paint: gray.lighten(20%), thickness: scaled-line-thickness * 1pt), + ) + } + } + } + + // Collect consonants by cell + let cell-consonants = (:) + for consonant in consonants-to-plot.clusters() { + if consonant in consonant-data { + let info = consonant-data.at(consonant) + let key = str(info.place) + "-" + str(info.manner) + + if key not in cell-consonants { + cell-consonants.insert(key, (voiceless: none, voiced: none)) + } + + if info.voicing { + cell-consonants.at(key).voiced = consonant + } else { + cell-consonants.at(key).voiceless = consonant + } + } + } + + // Special handling for /w/ (labiovelar approximant) + // If /w/ is present but /ɰ/ (velar approximant) is not, show /w/ in both bilabial and velar columns + if consonants-to-plot.contains("w") and not consonants-to-plot.contains("ɰ") { + let velar-approx-key = "7-6" // place 7 (velar), manner 6 (approximant) + if velar-approx-key not in cell-consonants { + cell-consonants.insert(velar-approx-key, (voiceless: none, voiced: none)) + } + cell-consonants.at(velar-approx-key).voiced = "w" + } + + // Collect aspirated plosives if enabled + let cell-aspirated-plosives = (:) + if aspirated { + for asp-plosive in aspirated-plosive-data.keys() { + if aspirated-plosives-to-plot.contains(asp-plosive) { + let info = aspirated-plosive-data.at(asp-plosive) + let key = str(info.place) + + if key not in cell-aspirated-plosives { + cell-aspirated-plosives.insert(key, (voiceless: none, voiced: none)) + } + + // Aspirated plosives are always voiceless + cell-aspirated-plosives.at(key).voiceless = asp-plosive + } + } + } + + // Collect affricates if enabled + let cell-affricates = (:) + if affricates { + // Strip tie bars from input (user might input t͡s, we display as ts) + let affricates-cleaned = affricates-to-plot.replace("͡", "") + + for affricate in affricate-data.keys() { + if affricates-cleaned.contains(affricate) { + let info = affricate-data.at(affricate) + let key = str(info.place) + + if key not in cell-affricates { + cell-affricates.insert(key, (voiceless: none, voiced: none)) + } + + if info.voicing { + cell-affricates.at(key).voiced = affricate + } else { + cell-affricates.at(key).voiceless = affricate + } + } + } + } + + // Collect aspirated affricates if both enabled + let cell-aspirated-affricates = (:) + if aspirated and affricates { + for asp-affricate in aspirated-affricate-data.keys() { + if aspirated-affricates-to-plot.contains(asp-affricate) { + let info = aspirated-affricate-data.at(asp-affricate) + let key = str(info.place) + + if key not in cell-aspirated-affricates { + cell-aspirated-affricates.insert(key, (voiceless: none, voiced: none)) + } + + // Aspirated affricates are always voiceless + cell-aspirated-affricates.at(key).voiceless = asp-affricate + } + } + } + + // Draw consonants in cells + for (key, pair) in cell-consonants { + let parts = key.split("-") + let orig-col = int(parts.at(0)) + let orig-manner = int(parts.at(1)) + + // Skip if column or row was deleted + if str(orig-col) in col-remap and str(orig-manner) in manner-to-row { + let col = col-remap.at(str(orig-col)) + let row = manner-to-row.at(str(orig-manner)) + + let col-width = column-widths.at(col) + let cell-x = col-starts.at(col) + let cell-y = total-height / 2 - scaled-label-height - (row * scaled-cell-height) + let cell-center-x = cell-x + (col-width / 2) + let cell-center-y = cell-y - (scaled-cell-height / 2) + + // Check if this is a sonorant manner (not contrastive for voicing, should be centered) + // Manners: 1=Nasal, 2=Trill, 3=Tap/Flap, 6=Approximant, 7=Lateral Approximant + let is-sonorant = orig-manner in (1, 2, 3, 6, 7) + + if is-sonorant { + // Center the consonant (sonorants are always voiced in typical inventories) + if pair.voiced != none { + let pos = (cell-center-x, cell-center-y) + circle(pos, radius: scaled-circle-radius, fill: white, stroke: none) + content(pos, context text(size: scaled-font-size * 1pt, font: phonokit-font.get(), pair.voiced), anchor: "center") + } + // In rare cases where voiceless sonorants exist, also center them + if pair.voiceless != none { + let pos = (cell-center-x, cell-center-y) + circle(pos, radius: scaled-circle-radius, fill: white, stroke: none) + content(pos, context text(size: scaled-font-size * 1pt, font: phonokit-font.get(), pair.voiceless), anchor: "center") + } + } else { + // Obstruents: use left/right positioning for voicing contrast + let offset = col-width * 0.25 + + if pair.voiceless != none { + let pos = (cell-center-x - offset, cell-center-y) + circle(pos, radius: scaled-circle-radius, fill: white, stroke: none) + content(pos, context text(size: scaled-font-size * 1pt, font: phonokit-font.get(), pair.voiceless), anchor: "center") + } + + if pair.voiced != none { + let pos = (cell-center-x + offset, cell-center-y) + circle(pos, radius: scaled-circle-radius, fill: white, stroke: none) + content(pos, context text(size: scaled-font-size * 1pt, font: phonokit-font.get(), pair.voiced), anchor: "center") + } + } + } + } + + // Draw aspirated plosives + if aspirated and aspirated-plosive-row >= 0 { + for (key, pair) in cell-aspirated-plosives { + let orig-col = int(key) + if str(orig-col) in col-remap { + let col = col-remap.at(str(orig-col)) + let row = aspirated-plosive-row + + let col-width = column-widths.at(col) + let cell-x = col-starts.at(col) + let cell-y = total-height / 2 - scaled-label-height - (row * scaled-cell-height) + let cell-center-x = cell-x + (col-width / 2) + let cell-center-y = cell-y - (scaled-cell-height / 2) + + let offset = col-width * 0.25 + + if pair.voiceless != none { + let pos = (cell-center-x - offset, cell-center-y) + circle(pos, radius: scaled-circle-radius, fill: white, stroke: none) + content(pos, context text(size: scaled-font-size * 1pt, font: phonokit-font.get(), pair.voiceless), anchor: "center") + } + } + } + } + + // Draw affricates + if affricates and affricate-row >= 0 { + for (key, pair) in cell-affricates { + let orig-col = int(key) + if str(orig-col) in col-remap { + let col = col-remap.at(str(orig-col)) + let row = affricate-row + + let col-width = column-widths.at(col) + let cell-x = col-starts.at(col) + let cell-y = total-height / 2 - scaled-label-height - (row * scaled-cell-height) + let cell-center-x = cell-x + (col-width / 2) + let cell-center-y = cell-y - (scaled-cell-height / 2) + + let offset = col-width * 0.25 + + if pair.voiceless != none { + let pos = (cell-center-x - offset, cell-center-y) + circle(pos, radius: scaled-circle-radius, fill: white, stroke: none) + content(pos, context text(size: scaled-font-size * 1pt, font: phonokit-font.get(), pair.voiceless), anchor: "center") + } + + if pair.voiced != none { + let pos = (cell-center-x + offset, cell-center-y) + circle(pos, radius: scaled-circle-radius, fill: white, stroke: none) + content(pos, context text(size: scaled-font-size * 1pt, font: phonokit-font.get(), pair.voiced), anchor: "center") + } + } + } + } + + // Draw aspirated affricates + if aspirated and affricates and aspirated-affricate-row >= 0 { + for (key, pair) in cell-aspirated-affricates { + let orig-col = int(key) + if str(orig-col) in col-remap { + let col = col-remap.at(str(orig-col)) + let row = aspirated-affricate-row + + let col-width = column-widths.at(col) + let cell-x = col-starts.at(col) + let cell-y = total-height / 2 - scaled-label-height - (row * scaled-cell-height) + let cell-center-x = cell-x + (col-width / 2) + let cell-center-y = cell-y - (scaled-cell-height / 2) + + let offset = col-width * 0.25 + + if pair.voiceless != none { + let pos = (cell-center-x - offset, cell-center-y) + circle(pos, radius: scaled-circle-radius, fill: white, stroke: none) + content(pos, context text(size: scaled-font-size * 1pt, font: phonokit-font.get(), pair.voiceless), anchor: "center") + } + } + } + } + }) + } +} diff --git a/packages/preview/phonokit/0.5.11/ex.typ b/packages/preview/phonokit/0.5.11/ex.typ new file mode 100644 index 0000000000..eefddd5d06 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/ex.typ @@ -0,0 +1,408 @@ +// Linguistic example environment with automatic numbering +// Similar to linguex's \ex. command in LaTeX +// +// Create a numbered linguistic example +// +// Generates numbered examples (1), (2), etc. similar to linguex in LaTeX. +// +// Arguments: +// - body (content): The example content +// - number-dy (length): Vertical offset for the number (optional; default: 0.4em) +// - caption (string): Caption for outline (hidden in document; optional) +// - title (content): Optional title shown on the same line as the number +// - labels (array): Optional labels for sub-examples in list mode (e.g., (, )) +// - columns (array): Optional column widths for tabular cells (data columns only) +// +// Returns: Numbered example that can be labeled and referenced +// +// Smart modes — detected automatically from body content: +// +// Single item (auto-numbered): +// #ex[Some example content] +// +// Single item with tabular cells (& separator): +// #ex[#ipa("/anba/") & #a-r & #ipa("[amba]")] +// +// Sub-examples with list syntax (auto-lettered): +// #ex[ +// - First example +// - Second example +// ] +// +// Sub-examples with tabular cells (& separator): +// #ex(labels: (, ), columns: (5em, 2em, 5em))[ +// - #ipa("/anba/") & #a-r & #ipa("[amba]") +// - #ipa("/anka/") & #a-r & #ipa("[aNka]") +// ] +// +// Legacy table mode (when body is a table — backward compatible): +// #ex()[ +// #table( +// columns: (2em, 2em, 5em, 2em, 5em), +// stroke: none, align: left, +// [#ex-num-label()], [#subex-label()], [#ipa("/anba/")], [#a-r], [#ipa("[amba]")], +// [], [#subex-label()], [#ipa("/anka/")], [#a-r], [#ipa("[aNka]")], +// ) +// ] + +// Counters +#let example-counter = counter("linguistic-example") +#let subex-counter = counter("linguistic-subexample") + +// Alphabet for sub-example lettering (a, b, c...) +#let letters = "abcdefghijklmnopqrstuvwxyz" + +// Split content at & characters into an array of cell contents. +// Walks the content tree, accumulating children into cells. +// Trims leading/trailing space nodes from each cell. +#let _split-cells(body) = { + let children = if body.has("children") { body.children } else { (body,) } + let cells = () + let current = () + for child in children { + if child.has("text") and child.text.contains("&") { + // Found separator — finalize current cell + cells.push(current) + current = () + } else { + current.push(child) + } + } + cells.push(current) // last cell + // Trim leading/trailing spaces from each cell and join into content + cells.map(cell => { + let trimmed = cell + // Trim leading spaces + while trimmed.len() > 0 and trimmed.first().func() == [ ].func() and trimmed.first().has("text") and trimmed.first().text.trim() == "" { + trimmed = trimmed.slice(1) + } + // Trim trailing spaces + while trimmed.len() > 0 and trimmed.last().func() == [ ].func() and trimmed.last().has("text") and trimmed.last().text.trim() == "" { + trimmed = trimmed.slice(0, trimmed.len() - 1) + } + trimmed.join() + }) +} + +// Check if content contains an & text node +#let _has-ampersand(body) = { + let children = if body.has("children") { body.children } else { (body,) } + children.any(c => c.has("text") and c.text.contains("&")) +} + +// Classify body content to determine rendering mode +// Typst's `- item` syntax creates list.item elements in a sequence (not wrapped in a list) +#let _classify-body(body) = { + if body.func() == list.item { return "list" } + if body.func() == table { return "legacy" } + if body.has("children") { + for child in body.children { + if child.func() == list.item { return "list" } + if child.func() == table { return "legacy" } + } + } + "single" +} + +// Extract list items from body (may be nested in a sequence) +#let _extract-items(body) = { + if body.func() == list.item { return (body,) } + if body.has("children") { + return body.children.filter(c => c.func() == list.item) + } + () +} + +// Build a grid from list items with auto numbering and lettering +// Supports & cell separator for tabular data +#let _build-subex-grid(items, labels, columns, numbered: true) = { + // Check if any item has tabular cells + let has-tabular = items.any(item => _has-ampersand(item.body)) + + let cells = () + let n-data-cols = 1 // default: single content column + + for (i, item) in items.enumerate() { + // Column 1: example number on first row only (skip when title handles numbering) + if numbered { + let num-cell = if i == 0 { + context { + subex-counter.update(0) + example-counter.step() + [(#(example-counter.get().first() + 1))] + } + } else { [] } + cells.push(num-cell) + } + + // Column 2: sub-example letter (a., b., c.) + let letter-fig = figure( + box(baseline: 0pt, context { + set par(first-line-indent: 0em) + subex-counter.step() + let n = subex-counter.get().first() + [#letters.at(n).] + }), + kind: "linguistic-subexample", + supplement: none, + numbering: none, + ) + // Attach label if provided (label must immediately follow figure, no whitespace) + let letter-cell = if labels != () and i < labels.len() { + [#letter-fig#labels.at(i)] + } else { + letter-fig + } + + cells.push(letter-cell) + + if has-tabular { + let data-cells = _split-cells(item.body) + n-data-cols = calc.max(n-data-cols, data-cells.len()) + for cell in data-cells { + cells.push(cell) + } + // Pad with empty cells if this row has fewer columns + let pad = n-data-cols - data-cells.len() + for _ in range(pad) { cells.push([]) } + } else { + cells.push(item.body) + } + } + + // Build column spec + let data-col-spec = if columns != () { + columns + } else if has-tabular { + range(n-data-cols).map(_ => auto) + } else { + (1fr,) + } + + let col-spec = if numbered { + (2em, 2em, ..data-col-spec) + } else { + (2em, ..data-col-spec) + } + + grid( + columns: col-spec, + row-gutter: 1em, + column-gutter: 0.5em, + align: left + bottom, + ..cells, + ) +} + +// Main example function +#let ex( + number-dy: 0.4em, + caption: none, + title: none, + labels: (), + columns: (), + body, +) = { + // Build the smart body content (list, single, or legacy) + // numbered: false when title branch handles the example number + let _build-smart-body(body, labels, columns, numbered: true) = { + let mode = _classify-body(body) + if mode == "list" { + let items = _extract-items(body) + _build-subex-grid(items, labels, columns, numbered: numbered) + } else if mode == "single" and _has-ampersand(body) { + let data-cells = _split-cells(body) + let data-col-spec = if columns != () { columns } else { + range(data-cells.len()).map(_ => auto) + } + grid( + columns: data-col-spec, + column-gutter: 0.5em, + align: left + bottom, + ..data-cells, + ) + } else { + body + } + } + + let content = if title != none { + // Title case: (num | title) / ([] | smart body) + let num = context { + subex-counter.update(0) + example-counter.step() + [(#(example-counter.get().first() + 1))] + } + let smart-body = _build-smart-body(body, labels, columns, numbered: false) + grid( + columns: (auto, 1fr), + column-gutter: 0.75em, + row-gutter: 0.3em, + align: (left + top, left + top), + num, title, + [], smart-body, + ) + } else { + let mode = _classify-body(body) + if mode == "list" { + // List mode: auto number + auto letter sub-examples (with optional & cells) + let items = _extract-items(body) + _build-subex-grid(items, labels, columns) + } else if mode == "single" { + // Single-item mode: auto number to the left + let num = context { + subex-counter.update(0) + example-counter.step() + [(#(example-counter.get().first() + 1))] + } + // Check for tabular cells in single-item mode + if _has-ampersand(body) { + let data-cells = _split-cells(body) + let data-col-spec = if columns != () { columns } else { + range(data-cells.len()).map(_ => auto) + } + grid( + columns: (2em, ..data-col-spec), + column-gutter: 0.5em, + align: left + bottom, + num, ..data-cells, + ) + } else { + grid( + columns: (auto, 1fr), + column-gutter: 0.75em, + align: (left + bottom, left + bottom), + num, body, + ) + } + } else { + // Legacy table mode: user manages numbering via ex-num-label() / subex-label() + let step = context { + subex-counter.update(0) + example-counter.step() + [] + } + grid( + columns: (auto, 1fr), + column-gutter: 0pt, + align: (left + top, left + top), + step, body, + ) + } + } + figure( + align(left, content), + caption: if caption != none { caption } else { none }, + outlined: caption != none, + kind: "linguistic-example", + supplement: none, + numbering: "(1)", + placement: none, + gap: 0pt, + ) +} + +// Display the current example number inside an ex() body. +// +// Use as the first-column cell of a 3-column table (num | sub-label | content) +// when no title is provided. Because it lives in the same table, it can share +// bottom alignment with the sub-example labels and the sentence text. +// +// Example: +// #ex(caption: "Example")[ +// #table( +// columns: 3, +// stroke: none, +// align: (left + bottom, left + bottom, left + top), +// [#ex-num-label()], [#subex-label()], [sentence a], +// [], [#subex-label()], [sentence b], +// ) +// ] +#let ex-num-label() = { + // No figure wrapper — a plain box behaves consistently with subex-label() + // in table cells without requiring explicit bottom alignment. + box(baseline: 0pt, context { + set par(first-line-indent: 0em) + let n = example-counter.get().first() + [(#n)] + }) +} + +// Create a sub-example label for use in tables +// +// Generates automatic lettering (a., b., c., ...) for table rows. +// Place in the first column of each row and attach a label after it. +// +// Returns: Labelable letter marker (a., b., c., ...) +// +// Example: +// #ex(caption: "A phonology example")[ +// #table( +// columns: 4, +// stroke: none, +// align: left, +// [#subex-label()], [#ipa("/anba/")], [#a-r], [#ipa("[amba]")], +// [#subex-label()], [#ipa("/anka/")], [#a-r], [#ipa("[aNka]")], +// ) +// ] +// +// See @ex-phon2, @ex-anba, and @ex-anka. +#let subex-label() = { + // Figure must be outermost so labels attach to it (not to context) + // Box with baseline ensures proper vertical alignment in table cells + // Reset first-line-indent to avoid misalignment in documents with paragraph indentation + figure( + box(baseline: 0pt, context { + set par(first-line-indent: 0em) + subex-counter.step() + let n = subex-counter.get().first() + // get() returns value BEFORE step, so n=0,1,2... gives a,b,c... + [#letters.at(n).] + }), + kind: "linguistic-subexample", + supplement: none, + numbering: none, + ) +} + +// Show rules for linguistic examples +// +// Apply this to enable proper reference formatting for ex() and subex-label(). +// References render as (1), (1a), (1b), etc. +// +// Usage: #show: ex-rules +#let ex-rules(doc) = { + show ref: it => { + let el = it.element + if el != none and el.func() == figure { + if el.kind == "linguistic-example" { + // Reference to main example: (1) + // at() returns value before step, so add 1 + link(el.location(), context { + let loc = el.location() + let num = example-counter.at(loc).first() + 1 + [(#num)] + }) + } else if el.kind == "linguistic-subexample" { + // Reference to sub-example: (1a) + // Subex is inside parent ex, so example-counter already stepped (no +1) + // Subex counter: at() returns value before step (0,1,2...) + link(el.location(), context { + let loc = el.location() + let parent-num = example-counter.at(loc).first() + let letter-num = subex-counter.at(loc).first() + let letter = letters.at(letter-num) + [(#parent-num#letter)] + }) + } else { + it + } + } else { + it + } + } + // Hide captions in document (they still appear in outline) + show figure.where(kind: "linguistic-example"): it => it.body + show figure.where(kind: "linguistic-subexample"): it => it.body + doc +} diff --git a/packages/preview/phonokit/0.5.11/extras.typ b/packages/preview/phonokit/0.5.11/extras.typ new file mode 100644 index 0000000000..bd2e0a88e8 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/extras.typ @@ -0,0 +1,39 @@ + +// NOTE: -- A collection of arrows +#let a-r-large = text(font: "New Computer Modern", size: 1.5em)[#h(1em)#sym.arrow.r#h(1em)] +#let a-r = text(font: "New Computer Modern", size: 1em)[#sym.arrow.r] +#let a-l = text(font: "New Computer Modern")[#sym.arrow.l] +#let a-u = text(font: "New Computer Modern")[#sym.arrow.t] +#let a-d = text(font: "New Computer Modern")[#sym.arrow.b] +#let a-ud = text(font: "New Computer Modern")[#sym.arrow.t.b] +#let a-lr = text(font: "New Computer Modern")[#sym.arrow.l.r] +#let a-sr = text(font: "New Computer Modern")[#sym.arrow.r.squiggly] +#let a-sl = text(font: "New Computer Modern")[#sym.arrow.l.squiggly] + +// NOTE: -- Function for context underline +#let blank(width: 2em) = box( + width: width, + height: 0.8em, + baseline: 50%, + stroke: (bottom: 0.5pt + black), +) + +// NOTE: -- Greek symbols (upright, for phonological notation) +#let alpha = sym.alpha +#let beta = sym.beta +#let gamma = sym.gamma +#let delta = sym.delta +#let lambda = sym.lambda +#let mu = sym.mu +#let phi = sym.phi +#let pi = sym.pi +#let sigma = sym.sigma +#let tau = sym.tau +#let omega = sym.omega +#let cap-phi = sym.Phi +#let cap-sigma = sym.Sigma +#let cap-omega = sym.Omega + +// NOTE: Extrametricality +#let extra(content) = [⟨#content⟩] + diff --git a/packages/preview/phonokit/0.5.11/features.typ b/packages/preview/phonokit/0.5.11/features.typ new file mode 100644 index 0000000000..1e9e1d983d --- /dev/null +++ b/packages/preview/phonokit/0.5.11/features.typ @@ -0,0 +1,2137 @@ +// Features module - Distinctive feature matrices from Hayes (2009) +// Based on Hayes, B. (2009). Introductory Phonology. Wiley-Blackwell. + +#import "ipa.typ": ipa-to-unicode +#import "_config.typ": phonokit-font +#import "ui-lang.typ": resolve-ui-lang, ui-lang-error, ui-feature-label + +// Function for feature matrices in SPE notation +/// Display feature matrix in SPE-style notation +/// +/// Creates a bracketed vertical list of features using math.vec. +/// Handles both individual arguments and comma-separated strings. +/// +/// Arguments: +/// - args (variadic): Feature specifications (e.g., "+consonantal", "-sonorant") +/// Can be passed as separate arguments or as a single comma-separated string +/// +/// Returns: Formatted feature matrix with proper spacing to prevent overlaps +/// +/// Example: +/// ``` +/// #feat("+consonantal", "-sonorant", "+voice") +/// #feat("+cons,-son,+voice") // comma-separated also works +/// ``` +#let feat(..args) = context { + let items = args.pos() + + // 1. Split string if comma-separated + if items.len() == 1 and type(items.at(0)) == str and items.at(0).contains(",") { + items = items.at(0).split(",") + } + + // 2. Style the items + let features = items.map(i => { + let content = if type(i) == str { i.trim() } else { i } + text(font: phonokit-font.get(), size: 1em, content) + }) + + // 3. Use math.vec for perfect axis alignment + set math.vec(gap: 0.5em) + let matrix = math.vec(delim: "[", ..features) + + // 4. Use box with vertical padding to add space around matrices + // This prevents overlaps while keeping matrices inline-friendly + box( + baseline: 50%, + inset: (top: 0.5em, bottom: 0.5em), + matrix, + ) +} + +// Complete feature specifications for consonants and vowels +// Features: +, -, or 0 (not applicable/unspecified) +#let feature-data = ( + // CONSONANTS - Single place of articulation (Table 4.7) + // Bilabial + "p": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "b": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ɸ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "β": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "m": ( + consonantal: "+", + sonorant: "+", + continuant: "–", + delayed_release: "0", + approximant: "–", + tap: "–", + trill: "–", + nasal: "+", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ʙ": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "+", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + // Labiodental + "f": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "+", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "v": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "+", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ɱ": ( + consonantal: "+", + sonorant: "+", + continuant: "–", + delayed_release: "0", + approximant: "–", + tap: "–", + trill: "–", + nasal: "+", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "+", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ʋ": ( + consonantal: "–", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "–", + labiodental: "+", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + // Dental + "θ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "+", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ð": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "+", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + // Alveolar + "t": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "d": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "t͡s": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "d͡z": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "s": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "z": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "n": ( + consonantal: "+", + sonorant: "+", + continuant: "–", + delayed_release: "0", + approximant: "–", + tap: "–", + trill: "–", + nasal: "+", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "l": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "–", + lateral: "+", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ɾ": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "+", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "r": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "+", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "–", + strident: "–", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + // Postalveolar + "ʃ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ʒ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "t͡ʃ": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "d͡ʒ": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + // Retroflex + "ʈ": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "–", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ɖ": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "–", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ʂ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "–", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ʐ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "–", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ɳ": ( + consonantal: "+", + sonorant: "+", + continuant: "–", + delayed_release: "0", + approximant: "–", + tap: "–", + trill: "–", + nasal: "+", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "–", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ɭ": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "–", + distributed: "+", + strident: "+", + lateral: "+", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ɽ": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "+", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "–", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ɻ": ( + consonantal: "–", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "–", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + // Palatal + "c": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "+", + back: "–", + tense: "0", + ), + "ɟ": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "+", + back: "–", + tense: "0", + ), + "ç": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "+", + back: "–", + tense: "0", + ), + "ʝ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "+", + back: "–", + tense: "0", + ), + "ɲ": ( + consonantal: "+", + sonorant: "+", + continuant: "–", + delayed_release: "0", + approximant: "–", + tap: "–", + trill: "–", + nasal: "+", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "+", + back: "–", + tense: "0", + ), + "ʎ": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "+", + dorsal: "+", + high: "+", + low: "–", + front: "+", + back: "–", + tense: "0", + ), + "j": ( + consonantal: "–", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "+", + back: "–", + tense: "+", + ), + // Velar + "k": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "–", + back: "0", + tense: "0", + ), + "g": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "–", + back: "0", + tense: "0", + ), + "x": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "–", + back: "0", + tense: "0", + ), + "ɣ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "–", + back: "0", + tense: "0", + ), + "ŋ": ( + consonantal: "+", + sonorant: "+", + continuant: "–", + delayed_release: "0", + approximant: "–", + tap: "–", + trill: "–", + nasal: "+", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "–", + back: "0", + tense: "0", + ), + "ɰ": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "–", + back: "0", + tense: "0", + ), + // Uvular + "q": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "–", + low: "–", + front: "–", + back: "+", + tense: "0", + ), + "ɢ": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "–", + low: "–", + front: "–", + back: "+", + tense: "0", + ), + "χ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "–", + low: "–", + front: "–", + back: "+", + tense: "0", + ), + "ʁ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "–", + low: "–", + front: "–", + back: "+", + tense: "0", + ), + "ɴ": ( + consonantal: "+", + sonorant: "+", + continuant: "–", + delayed_release: "0", + approximant: "–", + tap: "–", + trill: "–", + nasal: "+", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "–", + low: "–", + front: "–", + back: "+", + tense: "0", + ), + "ʀ": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "+", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "–", + low: "–", + front: "–", + back: "+", + tense: "0", + ), + // Pharyngeal + "ħ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "+", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "–", + low: "+", + front: "–", + back: "+", + tense: "0", + ), + "ʕ": ( + consonantal: "+", + sonorant: "–", + continuant: "+", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "–", + low: "+", + front: "–", + back: "+", + tense: "0", + ), + // Glottal + "ʔ": ( + consonantal: "+", + sonorant: "–", + continuant: "–", + delayed_release: "–", + approximant: "–", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "+", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "h": ( + consonantal: "–", + sonorant: "–", + continuant: "–", + delayed_release: "+", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "–", + spread_gl: "+", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + "ɦ": ( + consonantal: "–", + sonorant: "–", + continuant: "–", + delayed_release: "+", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "+", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "–", + high: "0", + low: "0", + front: "0", + back: "0", + tense: "0", + ), + // Complex segments (Table 4.8) + "w": ( + consonantal: "–", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "+", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "–", + back: "+", + tense: "+", + ), + "ɥ": ( + consonantal: "–", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "+", + round: "+", + labiodental: "–", + coronal: "–", + anterior: "0", + distributed: "0", + strident: "0", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "+", + back: "–", + tense: "+", + ), + "ɹ": ( + consonantal: "+", + sonorant: "+", + continuant: "+", + delayed_release: "0", + approximant: "+", + tap: "–", + trill: "–", + nasal: "–", + voice: "+", + spread_gl: "–", + constr_gl: "–", + labial: "–", + round: "–", + labiodental: "–", + coronal: "+", + anterior: "+", + distributed: "+", + strident: "+", + lateral: "–", + dorsal: "+", + high: "+", + low: "–", + front: "+", + back: "–", + tense: "0", + ), + // VOWELS (Table 4.9) + // High tense + "i": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "+", + low: "–", + tense: "+", + front: "+", + back: "–", + round: "–", + ), + "y": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "+", + low: "–", + tense: "+", + front: "+", + back: "–", + round: "+", + ), + "ɨ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "+", + low: "–", + tense: "+", + front: "–", + back: "–", + round: "–", + ), + "ʉ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "+", + low: "–", + tense: "+", + front: "–", + back: "–", + round: "+", + ), + "ɯ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "+", + low: "–", + tense: "+", + front: "–", + back: "+", + round: "–", + ), + "u": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "+", + low: "–", + tense: "+", + front: "–", + back: "+", + round: "+", + ), + // High lax + "ɪ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "+", + low: "–", + tense: "–", + front: "+", + back: "–", + round: "–", + ), + "ʏ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "+", + low: "–", + tense: "–", + front: "+", + back: "–", + round: "+", + ), + "ʊ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "+", + low: "–", + tense: "–", + front: "–", + back: "+", + round: "+", + ), + // Mid tense + "e": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "+", + front: "+", + back: "–", + round: "–", + ), + "ø": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "+", + front: "+", + back: "–", + round: "+", + ), + "ɘ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "+", + front: "–", + back: "–", + round: "–", + ), + "ɵ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "+", + front: "–", + back: "–", + round: "+", + ), + "ɤ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "+", + front: "–", + back: "+", + round: "–", + ), + "o": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "+", + front: "–", + back: "+", + round: "+", + ), + // Mid lax + "ɛ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "–", + front: "+", + back: "–", + round: "–", + ), + "œ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "–", + front: "+", + back: "–", + round: "+", + ), + "ə": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "–", + front: "–", + back: "–", + round: "–", + ), + "ɞ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "–", + front: "–", + back: "–", + round: "+", + ), + "ʌ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "–", + front: "–", + back: "+", + round: "–", + ), + "ɔ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "–", + tense: "–", + front: "–", + back: "+", + round: "+", + ), + // Low + "æ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "+", + tense: "0", + front: "+", + back: "–", + round: "–", + ), + "ɶ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "+", + tense: "0", + front: "+", + back: "–", + round: "+", + ), + "a": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "+", + tense: "0", + front: "–", + back: "–", + round: "–", + ), + "ɑ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "+", + tense: "0", + front: "–", + back: "+", + round: "–", + ), + "ɒ": ( + syllabic: "+", + consonantal: "–", + sonorant: "+", + continuant: "+", + voice: "+", + high: "–", + low: "+", + tense: "0", + front: "–", + back: "+", + round: "+", + ), +) + +// Feature matrix display function +/// Display complete feature matrix for an IPA segment +/// +/// Takes an IPA symbol (Unicode or tipa-style) and displays its complete +/// distinctive feature specification from Hayes (2009). +/// +/// Arguments: +/// - segment (string): IPA symbol (e.g., "p", "i", "\\t s" for t͡s) +/// - all (bool): Show all features including 0 values (default: false) +/// +/// Returns: Formatted feature matrix in SPE-style notation +/// +/// Example: +/// ``` +/// #feat-matrix("p") +/// #feat-matrix("t \\t s") // affricate using tipa notation +/// #feat-matrix("i", all: true) // show all features including 0 +/// ``` +#let feat-matrix(segment, all: false, ui-lang: "en") = context { + let ui-locale = resolve-ui-lang(ui-lang) + if ui-locale == none { + return ui-lang-error(ui-lang) + } + + // Convert tipa notation to Unicode if needed + let symbol = ipa-to-unicode(segment).trim() + + // Look up features + if symbol not in feature-data { + return text(fill: red)[*Error:* No feature data for "#symbol"] + } + + let features = feature-data.at(symbol) + let feature-list = () + + // Build feature list with readable names + for (feature-name, value) in features { + if all or value != "0" { + let display-name = ui-feature-label(feature-name, ui-locale) + feature-list.push(value + display-name) + } + } + + // Display as inline block with top alignment to allow side-by-side placement + let phoneme = text(size: 1em, font: phonokit-font.get())[/#symbol/] + + // Build matrix manually for precise control over alignment + let features = feature-list.map(f => text(font: phonokit-font.get(), size: 0.8em)[#f]) + let content-stack = stack( + dir: ttb, + spacing: 0.55em, + ..features, + ) + + // Wrap in brackets with precise positioning + // Add horizontal padding inside brackets for better spacing + let matrix = box[ + $lr( + [#h(0.15em)#content-stack#h(0.15em)], + size: #100% + )$ + ] + + // Use box with baseline at bottom (100%) for top alignment + // Counter-intuitively, this makes the tops align + box(baseline: 100%)[ + #grid( + columns: 1, + row-gutter: 1.5em, + // Phoneme in large fixed box, anchored at bottom for consistent matrix starting point + // This ensures all matrices start at exactly the same vertical position + align(center, box(height: 2em, align(bottom, phoneme))), + // Feature matrix starts at fixed position below + align(center, matrix), + ) + ] +} diff --git a/packages/preview/phonokit/0.5.11/gallery/autoseg_example_1.png b/packages/preview/phonokit/0.5.11/gallery/autoseg_example_1.png new file mode 100644 index 0000000000000000000000000000000000000000..9370523b683d267284123c062e4f7e33a4603fa6 GIT binary patch literal 5349 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 literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.11/gallery/autoseg_example_1.typ b/packages/preview/phonokit/0.5.11/gallery/autoseg_example_1.typ new file mode 100644 index 0000000000..a2ec21a6e1 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/gallery/autoseg_example_1.typ @@ -0,0 +1,11 @@ +#import "@preview/phonokit:0.5.11": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, x: 1em)) + +#autoseg( + ("k", "\\ae", "n", "t"), + features: ("", "", "[+nas]", ""), + links: ((2, 1),), + spacing: 1.0, + arrow: false, +) + diff --git a/packages/preview/phonokit/0.5.11/gallery/autoseg_example_2.png b/packages/preview/phonokit/0.5.11/gallery/autoseg_example_2.png new file mode 100644 index 0000000000000000000000000000000000000000..58849e6555b8bfdee5227c216dcdc0c60a640b9e GIT binary patch literal 11274 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 literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.11/gallery/autoseg_example_2.typ b/packages/preview/phonokit/0.5.11/gallery/autoseg_example_2.typ new file mode 100644 index 0000000000..ad9a225a33 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/gallery/autoseg_example_2.typ @@ -0,0 +1,22 @@ +#import "@preview/phonokit:0.5.11": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, x: 1em)) + +#autoseg( + ("e", "b", "e"), + features: ("L", "", "H"), + spacing: 0.5, + tone: true, + baseline: 50%, + gloss: [], +) +#a-r +#autoseg( + ("e", "b", "e"), + features: ("L", "", "H"), + links: ((0, 2),), + spacing: 0.5, + baseline: 50%, + tone: true, + gloss: [èbě _pumpkin_], +) + diff --git a/packages/preview/phonokit/0.5.11/gallery/autoseg_example_3.png b/packages/preview/phonokit/0.5.11/gallery/autoseg_example_3.png new file mode 100644 index 0000000000000000000000000000000000000000..6128c4a22be892ac025f7ce40dba6967600524f5 GIT binary patch literal 57914 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 Ah4@+Fb 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 VNB_w%&TbGGVr%6Dr=Gi<^e-zCFlPV& literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.11/gallery/features_example.typ b/packages/preview/phonokit/0.5.11/gallery/features_example.typ new file mode 100644 index 0000000000..e4d5de5173 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/gallery/features_example.typ @@ -0,0 +1,5 @@ +#import "@preview/phonokit:0.5.11": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, x: 1em)) + +#feat-matrix("p") #feat-matrix("\\ae") #feat-matrix("\\t tS") #feat-matrix("i") + diff --git a/packages/preview/phonokit/0.5.11/gallery/grid_example.png b/packages/preview/phonokit/0.5.11/gallery/grid_example.png new file mode 100644 index 0000000000000000000000000000000000000000..052e5e706d30fa3cf566ef533a1cda1d61dcf35c GIT binary patch literal 5390 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 F{2xA{D~kXC literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.11/gallery/ipa_example.typ b/packages/preview/phonokit/0.5.11/gallery/ipa_example.typ new file mode 100644 index 0000000000..f53ac50e52 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/gallery/ipa_example.typ @@ -0,0 +1,17 @@ +#import "@preview/phonokit:0.5.11": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, x: 1em)) + +#ipa("/DIs \\s Iz \\s @ \\s sEn.t@ns/") + +#ipa("[N \\N R \\R \\I I Z \\Z ]") + +#ipa("[p \\h Ik \\* \\s \\t tS \\ae t \\s p \\r liz]") + +#ipa("['t \\h \\ae k,sI]") + +#ipa("[ \\!o || \\epsilonr k \\velar \\dental t ia]") + +#ipa("[ \\nh \\v lRKt \\palatal ]") + +#ipa("['lIR \\v \\darkl \\s 'b2R \\schwar ,flaI \\s 'oU.v \\schwar \\s DE \\*r ]") + diff --git a/packages/preview/phonokit/0.5.11/gallery/maxent_example.png b/packages/preview/phonokit/0.5.11/gallery/maxent_example.png new file mode 100644 index 0000000000000000000000000000000000000000..27eaef5b63a81c917f6eeb3061f7cf0a1855da28 GIT binary patch literal 131582 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%0Dj2!CbfCAdFs0)gs&(m^qJc+fI73< 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%0AtY#)Jfks|IcgA$BB&u_(xCQrBt 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

{c6^9PjX83?mCO~L`qi&QMhhI0EE(4g8AuYk@m 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

0P#r+p&+j|^a}d};BG%es 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 Bzdrx~ literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.11/gallery/maxent_example.typ b/packages/preview/phonokit/0.5.11/gallery/maxent_example.typ new file mode 100644 index 0000000000..689a86ffc1 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/gallery/maxent_example.typ @@ -0,0 +1,41 @@ +#import "@preview/phonokit:0.5.11": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, left: 1em, right: 2cm)) + +#maxent( + input: "kraTa", + candidates: ("[kra.Ta]", "[ka.Ta]", "[ka.ra.Ta]"), + constraints: ("Max", "Dep", "*Complex"), + weights: (2.5, 1.8, 1), + violations: ( + (0, 0, 1), + (1, 0, 0), + (0, 1, 0), + ), + visualize: true, // Show probability bars (default) +) + +#maxent( + input: "kraTa", + candidates: ("[kra.Ta]", "[ka.Ta]", "[ka.ra.Ta]"), + constraints: ("Max", "Dep", "*Complex"), + weights: (2.5, 1.8, 1), + violations: ( + (0, 0, 1), + (1, 0, 0), + (0, 1, 0), + ), + visualize: false, +) + +#maxent( + input: "kraTa", + candidates: ("[kra.Ta]", "[ka.Ta]", "[ka.ra.Ta]"), + constraints: ("Max", "Dep", "*Complex"), + weights: (2.5, 1.8, 1), + violations: ( + (0, 0, 1), + (1, 0, 0), + (0, 1, 0), + ), + sort: true, // Sort candidates by probability (most to least) +) diff --git a/packages/preview/phonokit/0.5.11/gallery/multi-tier_example.png b/packages/preview/phonokit/0.5.11/gallery/multi-tier_example.png new file mode 100644 index 0000000000000000000000000000000000000000..9288da4ff184b0f20ce4e35404b65bac7cf8a024 GIT binary patch literal 17166 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 Ml2(?gk~9hWU#Ku0`v3p{ literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.11/gallery/multi-tier_example.typ b/packages/preview/phonokit/0.5.11/gallery/multi-tier_example.typ new file mode 100644 index 0000000000..3c2a1f9ed3 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/gallery/multi-tier_example.typ @@ -0,0 +1,18 @@ +#import "@preview/phonokit:0.5.11": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, x: 1em)) + +#multi-tier( + levels: ( + ("O", "R", "", "O", "R", "O", "R"), + ("", "N1", "", "", "N2", "", "N3"), + ("", "x", "x", "x", "x", "x", "x"), + ("", "", "s", "t", "E", "m", ""), + ), + links: (("r2", "x2"),), + ipa: (3,), + arrows: (("t1", "s1"), ("r2", "r1")), + arrow-delinks: (1,), + spacing: 1, +) + +// Figure from Goad (2012); see phonokit.pdf for full reference. diff --git a/packages/preview/phonokit/0.5.11/gallery/ot_example.png b/packages/preview/phonokit/0.5.11/gallery/ot_example.png new file mode 100644 index 0000000000000000000000000000000000000000..61b0341e0c7926faa74139a8cda3017cc89d0a29 GIT binary patch literal 22397 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 QZSv|G8(N$z(Z@vo2U4MqkpKVy literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.11/gallery/ot_example.typ b/packages/preview/phonokit/0.5.11/gallery/ot_example.typ new file mode 100644 index 0000000000..9f1043fb1c --- /dev/null +++ b/packages/preview/phonokit/0.5.11/gallery/ot_example.typ @@ -0,0 +1,17 @@ +#import "@preview/phonokit:0.5.11": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, x: 1em)) + + +#tableau( + input: "kraTa", + candidates: ("[kra.Ta]", "[ka.Ta]", "[ka.ra.Ta]"), + constraints: ("Max", "Dep", "*Complex"), + violations: ( + ("", "", "*"), + ("*!", "", ""), + ("", "*!", ""), + ), + winner: 0, + dashed-lines: (0,), +) + diff --git a/packages/preview/phonokit/0.5.11/gallery/syllable_example.png b/packages/preview/phonokit/0.5.11/gallery/syllable_example.png new file mode 100644 index 0000000000000000000000000000000000000000..3fc5d0cdd5092325d6ee6bbbb34919197e5fb003 GIT binary patch literal 8740 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 literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.11/gallery/syllable_example.typ b/packages/preview/phonokit/0.5.11/gallery/syllable_example.typ new file mode 100644 index 0000000000..a1243e0fde --- /dev/null +++ b/packages/preview/phonokit/0.5.11/gallery/syllable_example.typ @@ -0,0 +1,6 @@ +#import "@preview/phonokit:0.5.11": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, x: 1em)) + +#syllable("\\t tS I t \\*", scale: 0.7) #h(1em) #syllable("\\t tS \\ae t", scale: 0.7) + + diff --git a/packages/preview/phonokit/0.5.11/gallery/vowels_example.png b/packages/preview/phonokit/0.5.11/gallery/vowels_example.png new file mode 100644 index 0000000000000000000000000000000000000000..b6215b5b3c085511331971999108a2b7ad11d1a6 GIT binary patch literal 24100 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 literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.11/gallery/vowels_example.typ b/packages/preview/phonokit/0.5.11/gallery/vowels_example.typ new file mode 100644 index 0000000000..04c2d37106 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/gallery/vowels_example.typ @@ -0,0 +1,17 @@ +#import "@preview/phonokit:0.5.11": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, x: 1em)) + +#vowels( + "english", + arrows: ( + ("a", "U"), + ("a", "I"), + ("e", "I"), + ("O", "I"), + ("o", "U"), + ), + arrow-color: blue.lighten(60%), + curved: true, + highlight: ("a", "e", "o", "O"), + highlight-color: blue.lighten(80%), +) diff --git a/packages/preview/phonokit/0.5.11/gallery/word_example.png b/packages/preview/phonokit/0.5.11/gallery/word_example.png new file mode 100644 index 0000000000000000000000000000000000000000..d4a112dcbb7b72b8b1f41ea2a8325f9326092702 GIT binary patch literal 22635 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�*Yx*owLhj>0M@=jzXQl}I@}vDHGMkrRPi>|(Cs&12*9roGrC`nlVz zlcZy?^FU39Y5CmH@Qa^h^n>xq;LzSud{GYak^jZf+1#>PA=-tZTqRFzIkH%c#9)3$5gs1o#0{Cgt-k25Tdye%lGXB2Q)|xdLaK~FfWLCa=8|R++uKsCT zEVd%bkMen%m?^o8XxP8hZA@D&7q^1^9cT$N8hbXm7xqAPn&UkVm~32J`e*78g^CPO zT#JFi<_D=O5>aIm zo8>&HBcKB%HyoC`14^^Fwi66pOE${!n5LrKMHFhgD0i#)oM1ZOPWXt;R%))sVVi`d z+lop(W!3IH<>Rx_4=!_`8jUmfO~j;CE^u6StHV-P@q|_Ra06DhqX5B&LiLuY`1AB+vL%vp-qp zl&m~8&Z0dQL~3pR>gisP@<-*sQDZn19#_=xjq)E??eZ*5ZvN}5kQ=YC6#p<)e;WpM zH=p&i2bCUn&qCvhp0Zxn7S~SHUJP`H`FJ>nEzRb;#Ar-c!A4iVZWcJg_c! 0 and node.children.all(c => c.children.len() == 0) +} + +// ── All-non-leaf predicate ────────────────────────────────────────────────── +// True when every child of `node` is an internal node (has its own children). +// e.g. vocalic → [V-place, aperture]. These groups stagger vertically to +// avoid horizontal label collisions between wide class-node names. +#let _all-nonleaves(node) = { + node.children.len() > 0 and node.children.all(c => c.children.len() > 0) +} + +// ── Recursive subtree width ───────────────────────────────────────────────── +#let _tree-w(node) = { + if node.children.len() == 0 { return _leaf-w } + let gap = if _all-leaves(node) { _leaf-h-gap } else if node.kind == "root" { _root-h-gap } else { _h-gap } + let cw = node.children.map(c => _tree-w(c)) + calc.max(cw.sum() + (node.children.len() - 1) * gap, _leaf-w) +} + +// ── Recursive layout → flat list of positioned entries ────────────────────── +// Returns array of (label, kind, feats, x, y, par) dictionaries. +#let _layout(node, x0, y, par) = { + let w = _tree-w(node) + let my-x = x0 + w / 2 + let me = ((label: node.label, kind: node.kind, feats: node.at("features", default: ()), x: my-x, y: y, par: par),) + let gap = if _all-leaves(node) { _leaf-h-gap } else if node.kind == "root" { _root-h-gap } else { _h-gap } + // Count consecutive leading leaves (leaves before the first non-leaf child). + let n-leading = { + let count = 0 + for c in node.children { + if c.children.len() == 0 { count = count + 1 } else { break } + } + count + } + let cx = x0 + let out = me + for (i, child) in node.children.enumerate() { + let cw = _tree-w(child) + let all-prev-leaves = node.children.slice(0, i).all(c => c.children.len() == 0) + // Leaf children are staggered vertically by their rank among leaf siblings. + // Non-leaf children (e.g. C-place) always stay at the standard level. + let child-y = if child.children.len() == 0 { + let leaf-rank = node.children.slice(0, i).filter(c => c.children.len() == 0).len() + // "Leading" leaves: all siblings before this one are also leaves. + // • n-leading == 1: single leaf before a non-leaf (e.g. [nasal] before oral + // cavity) — use _mixed-leaf-vg for vertical position. + // • n-leading >= 2: multiple leaves (e.g. voice/continuant before C-place) — + // use _mixed-leaf-vg so the stagger distributes them around the non-leaf level. + // "Sandwiched" leaves: a non-leaf precedes this leaf → push below non-leaf level. + let base = if _all-leaves(node) { _v-gap } else if all-prev-leaves { _mixed-leaf-vg } else { + _v-gap + _stagger-dy * 0.3 + } + let step = if all-prev-leaves and not _all-leaves(node) { _ml-stagger-dy } else { _stagger-dy } + y - base - leaf-rank * step + } else { + // Non-leaf child: stagger vertically when all siblings are also non-leaves + // AND the parent is not the root node. Root's direct children (laryngeal, + // oral cavity) must stay at the same tier by convention. Deeper all-non-leaf + // groups (e.g. V-place + aperture under vocalic) do get staggered. + if _all-nonleaves(node) and node.kind != "root" { + y - _v-gap - i * _nonleaf-stagger-dy + } else if not _all-leaves(node) and not _all-nonleaves(node) { + y - _mixed-nonleaf-vg // mixed group: non-leaf drops further (e.g. C-place) + } else { + y - _v-gap + } + } + // Single leading leaf: shift further left so the line from the parent is + // more diagonal rather than nearly vertical next to the root label. + let cx-adj = if ( + child.children.len() == 0 and all-prev-leaves and not _all-leaves(node) and n-leading == 1 + ) { -0.40 } else { 0 } + out = out + _layout(child, cx + cx-adj, child-y, (my-x, y)) + cx = cx + cw + gap + } + out +} + +// ── Segment presets (Clements & Hume 1995) ────────────────────────────────── +// Pre-built spec dicts for common segments. Used by the `ph` parameter in +// geom() and as a `ph` key in geom-group spec dicts, e.g. (ph: "a"). +// Height is encoded by aperture (no features = high), front/back by V-place. +#let _presets = ( + // NOTE: The most common vowels: + "i": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + coronal: true, + aperture: ("-", "-", "-"), + segment: "i", + ), + "e": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + coronal: true, + aperture: ("-", "+", "-"), + segment: "e", + ), + "E": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + coronal: true, + aperture: ("-", "+", "+"), + segment: "E", + ), + "a": (root: ("+son", "+approx", "+vocoid"), vocalic: true, aperture: ("+", "+", "+"), segment: "a"), + "o": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + labial: true, + dorsal: true, + aperture: ("-", "+", "-"), + segment: "o", + ), + "O": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + labial: true, + dorsal: true, + aperture: ("-", "+", "+"), + segment: "O", + ), + "u": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + labial: true, + dorsal: true, + aperture: ("-", "-", "-"), + segment: "u", + ), + "I": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + coronal: true, + tense: "-", + aperture: ("-", "-", "-"), + segment: "I", + ), + "U": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + labial: true, + dorsal: true, + tense: "-", + aperture: ("-", "-", "-"), + segment: "U", + ), + // Additional vowels — high confidence: + "y": ( + // ø̈ close front rounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + coronal: true, + labial: true, + aperture: ("-", "-", "-"), + segment: "y", + ), + "W": ( + // ɯ close back unrounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + dorsal: true, + aperture: ("-", "-", "-"), + segment: "W", + ), + "7": ( + // ɤ close-mid back unrounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "+", + dorsal: true, + aperture: ("-", "+", "-"), + segment: "7", + ), + "\\o": ( + // ø close-mid front rounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "+", + coronal: true, + labial: true, + aperture: ("-", "+", "-"), + segment: "\\o", + ), + "\\oe": ( + // œ open-mid front rounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "-", + coronal: true, + labial: true, + aperture: ("-", "+", "+"), + segment: "\\oe", + ), + "2": ( + // ʌ open-mid back unrounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "-", + dorsal: true, + aperture: ("-", "+", "+"), + segment: "2", + ), + "A": ( + // ɑ open back unrounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + dorsal: true, + aperture: ("+", "+", "+"), + segment: "A", + ), + "6": ( + // ɒ open back rounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + labial: true, + dorsal: true, + aperture: ("+", "+", "+"), + segment: "6", + ), + // Flagged vowels — central/near-open: place analysis is theory-dependent. + "@": ( + // ə mid central — placeless in CH (no V-place node), aperture ("-","+","-") + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + aperture: ("-", "+", "-"), + segment: "@", + ), + "1": ( + // ɨ close central unrounded — placeless in CH + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + aperture: ("-", "-", "-"), + segment: "1", + ), + "0": ( + // ʉ close central rounded — labial only, no dorsal/coronal V-place + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + labial: true, + aperture: ("-", "-", "-"), + segment: "0", + ), + "\\ae": ( + // æ near-open front — aperture approximated as open-mid ("-","+","+"); same as /ɛ/ in CH + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + coronal: true, + aperture: ("-", "+", "+"), + segment: "\\ae", + ), + // NOTE: Some consonants: + "p": ( + root: ("-son", "-approx", "-vocoid"), + vocalic: false, + labial: true, + voice: "-", + segment: "p", + continuant: "-", + ), + "b": ( + root: ("-son", "-approx", "-vocoid"), + vocalic: false, + labial: true, + voice: "+", + segment: "b", + continuant: "-", + ), + "t": (root: ("-son", "-approx", "-vocoid"), coronal: true, anterior: "+", voice: "-", continuant: "-", segment: "t"), + "d": (root: ("-son", "-approx", "-vocoid"), coronal: true, anterior: "+", voice: "+", continuant: "-", segment: "d"), + "k": ( + root: ("-son", "-approx", "-vocoid"), + vocalic: false, + dorsal: true, + voice: "-", + segment: "k", + continuant: "-", + ), + "g": ( + root: ("-son", "-approx", "-vocoid"), + vocalic: false, + dorsal: true, + voice: "+", + segment: "g", + continuant: "-", + ), + "f": ( + root: ("-son", "-approx", "-vocoid"), + vocalic: false, + labial: true, + voice: "-", + segment: "f", + continuant: "+", + ), + "v": ( + root: ("-son", "-approx", "-vocoid"), + vocalic: false, + labial: true, + voice: "+", + segment: "v", + continuant: "+", + ), + "s": (root: ("-son", "-approx", "-vocoid"), coronal: true, anterior: "+", voice: "-", continuant: "+", segment: "s"), + "z": (root: ("-son", "-approx", "-vocoid"), coronal: true, anterior: "+", voice: "+", continuant: "+", segment: "z"), + // ʃ/ʒ: coronal [-anterior], NOT dorsal + "S": (root: ("-son", "-approx", "-vocoid"), coronal: true, anterior: "-", voice: "-", continuant: "+", segment: "S"), + "Z": (root: ("-son", "-approx", "-vocoid"), coronal: true, anterior: "-", voice: "+", continuant: "+", segment: "Z"), + // Affricates — [-continuant] following standard SPE/Kenstowicz analysis + "ts": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "+", + voice: "-", + continuant: ("-", "+"), + segment: "ts", + ), + "dz": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "+", + voice: "+", + continuant: ("-", "+"), + segment: "dz", + ), + "tS": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "-", + voice: "-", + continuant: ("-", "+"), + segment: "tS", + ), + "dZ": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "-", + voice: "+", + continuant: ("-", "+"), + segment: "dZ", + ), + "n": ( + root: ("+son", "-approx", "-vocoid"), + vocalic: false, + nasal: true, + coronal: true, + voice: "+", + segment: "n", + continuant: "-", + ), + "m": ( + root: ("+son", "-approx", "-vocoid"), + vocalic: false, + nasal: true, + labial: true, + voice: "+", + segment: "m", + continuant: "-", + ), + "N": ( + root: ("+son", "-approx", "-vocoid"), + vocalic: false, + nasal: true, + dorsal: true, + voice: "+", + segment: "N", + continuant: "-", + ), + // ɲ: palatal nasal = coronal [-anterior] + "\\N": (root: ("+son", "-approx", "-vocoid"), coronal: true, anterior: "-", nasal: "+", segment: "\\N"), + // Additional consonants — high confidence: + "j": (root: ("+son", "+approx", "-vocoid"), dorsal: true, continuant: "+", segment: "j"), // j palatal approximant + "h": (root: ("-son", "-approx", "-vocoid"), spread: true, continuant: "+", segment: "h"), // h glottal fricative + "?": (root: ("-son", "-approx", "-vocoid"), constricted: true, continuant: "-", segment: "?"), // ʔ glottal stop + "T": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "+", + distributed: true, + voice: "-", + continuant: "+", + segment: "T", + ), // θ voiceless dental fricative + "D": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "+", + distributed: true, + voice: "+", + continuant: "+", + segment: "D", + ), // ð voiced dental fricative + "x": (root: ("-son", "-approx", "-vocoid"), dorsal: true, voice: "-", continuant: "+", segment: "x"), // x voiceless velar fricative + "G": (root: ("-son", "-approx", "-vocoid"), dorsal: true, voice: "+", continuant: "+", segment: "G"), // ɣ voiced velar fricative + "F": (root: ("-son", "-approx", "-vocoid"), labial: true, voice: "-", continuant: "+", segment: "F"), // ɸ voiceless bilabial fricative + "B": (root: ("-son", "-approx", "-vocoid"), labial: true, voice: "+", continuant: "+", segment: "B"), // β voiced bilabial fricative + "V": (root: ("+son", "+approx", "-vocoid"), labial: true, continuant: "+", segment: "V"), // ʋ labiodental approximant + "M": (root: ("+son", "-approx", "-vocoid"), labial: true, nasal: true, voice: "+", continuant: "-", segment: "M"), // ɱ labiodental nasal + "\\:t": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "-", + distributed: true, + voice: "-", + continuant: "-", + segment: "\\:t", + ), // ʈ retroflex stop (vl) + "\\:d": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "-", + distributed: true, + voice: "+", + continuant: "-", + segment: "\\:d", + ), // ɖ retroflex stop (vd) + "\\:s": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "-", + distributed: true, + voice: "-", + continuant: "+", + segment: "\\:s", + ), // ʂ retroflex fricative (vl) + "\\:z": ( + root: ("-son", "-approx", "-vocoid"), + coronal: true, + anterior: "-", + distributed: true, + voice: "+", + continuant: "+", + segment: "\\:z", + ), // ʐ retroflex fricative (vd) + "\\:n": ( + root: ("+son", "-approx", "-vocoid"), + coronal: true, + anterior: "-", + distributed: true, + nasal: "+", + voice: "+", + continuant: "-", + segment: "\\:n", + ), // ɳ retroflex nasal + // Flagged consonants — place analysis is theory-dependent: + "r": (root: ("+son", "-approx", "-vocoid"), coronal: true, anterior: "+", voice: "+", continuant: "-", segment: "r"), // r alveolar trill — [-cont] following Kenstowicz (1994) + "l": (root: ("+son", "+approx", "-vocoid"), coronal: true, anterior: "+", voice: "+", continuant: "+", lateral: true, segment: "l"), // l alveolar lateral + "L": (root: ("+son", "+approx", "-vocoid"), coronal: true, anterior: "-", voice: "+", continuant: "+", lateral: true, segment: "L"), // ʎ palatal lateral approximant — palatal = coronal [−anterior], cf. ɲ/ç + "J": (root: ("-son", "-approx", "-vocoid"), dorsal: true, voice: "+", continuant: "+", segment: "J"), // ʝ voiced palatal fricative — NOTE: coronal vs. dorsal analysis contested; dorsal used here + "C": (root: ("-son", "-approx", "-vocoid"), coronal: true, anterior: "-", voice: "-", continuant: "+", segment: "C"), // ç voiceless palatal fricative — NOTE: strident not modelled (cf. /ʃ/) + // archiphoneme /T/: any stop — no place, no voice specified + "\\T": (root: ("-son", "-approx", "-vocoid"), continuant: "-", segment: "\\T"), + "\\C": (root: ("±son", "±approx", "-vocoid"), segment: "\\C"), + "\\V": (root: ("+son", "+approx", "+vocoid"), segment: "\\V"), +) + +// ── Segment presets (Sagey 1986) ───────────────────────────────────────────── +// Vowels only — consonants fall back to _presets in both models. The only +// consonant difference between models is where [lateral] attaches, which +// _build-tree handles from the `model` key (oral cavity vs. [coronal]). +// Height/backness encoded as dorsal sub-features; roundness as labial: ("round",). +// No aperture node. Note: [e]/[ɛ] and [o]/[ɔ] share the same basic features +// in Sagey (ATR/tense distinguishes them, which is not modelled here). +#let _presets-sagey = ( + "i": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + dorsal: ("+high", "-back"), + segment: "i", + ), + "e": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "+", + dorsal: ("-high", "-back"), + segment: "e", + ), + "E": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "-", + dorsal: ("-high", "-back"), + segment: "E", + ), + "a": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + dorsal: ("-high", "+lo"), + segment: "a", + ), + "o": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "+", + labial: ("round",), + dorsal: ("-high", "+back"), + segment: "o", + ), + "O": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "-", + labial: ("round",), + dorsal: ("-high", "+back"), + segment: "O", + ), + "u": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + labial: ("round",), + dorsal: ("+high", "+back"), + segment: "u", + ), + "I": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "-", + dorsal: ("+high", "-back"), + segment: "I", + ), + "U": ( + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "-", + labial: ("round",), + dorsal: ("+high", "+back"), + segment: "U", + ), + // Additional vowels — high confidence: + "y": ( + // y close front rounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + labial: ("round",), + dorsal: ("+high", "-back"), + segment: "y", + ), + "W": ( + // ɯ close back unrounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + dorsal: ("+high", "+back"), + segment: "W", + ), + "7": ( + // ɤ close-mid back unrounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "+", + dorsal: ("-high", "+back"), + segment: "7", + ), + "\\o": ( + // ø close-mid front rounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "+", + labial: ("round",), + dorsal: ("-high", "-back"), + segment: "\\o", + ), + "\\oe": ( + // œ open-mid front rounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "-", + labial: ("round",), + dorsal: ("-high", "-back"), + segment: "\\oe", + ), + "2": ( + // ʌ open-mid back unrounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + tense: "-", + dorsal: ("-high", "+back"), + segment: "2", + ), + "A": ( + // ɑ open back unrounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + dorsal: ("-high", "+lo", "+back"), + segment: "A", + ), + "6": ( + // ɒ open back rounded + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + labial: ("round",), + dorsal: ("-high", "+lo", "+back"), + segment: "6", + ), + // Flagged vowels — central/near-open: place analysis is theory-dependent. + "@": ( + // ə mid central — dorsal [-hi] only; back unspecified + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + dorsal: ("-high",), + segment: "@", + ), + "1": ( + // ɨ close central unrounded — dorsal [+hi], no back value + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + dorsal: ("+high",), + segment: "1", + ), + "0": ( + // ʉ close central rounded — [+hi] + labial, no back value + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + labial: ("round",), + dorsal: ("+high",), + segment: "0", + ), + "\\ae": ( + // æ near-open front — [-hi, +lo, -back]; +lo distinguishes from /ɛ/ (-hi, -back) + root: ("+son", "+approx", "+vocoid"), + vocalic: true, + vplace: true, + dorsal: ("-high", "+lo", "-back"), + segment: "\\ae", + ), +) + +// ── Build tree from spec dict ──────────────────────────────────────────────── +// Accepts a dict with the same keys as geom() (all optional, same defaults). +// Returns (tree: root-node-dict, is-vocoid: bool). +#let _build-tree(spec) = { + let root = spec.at("root", default: ()) + let laryngeal = spec.at("laryngeal", default: false) + let nasal = spec.at("nasal", default: false) + let spread = spec.at("spread", default: false) + let constricted = spec.at("constricted", default: false) + let voice = spec.at("voice", default: false) + let continuant = spec.at("continuant", default: false) + let labial = spec.at("labial", default: false) + let coronal = spec.at("coronal", default: false) + let anterior = spec.at("anterior", default: false) + let distributed = spec.at("distributed", default: false) + let dorsal = spec.at("dorsal", default: false) + let vocalic = spec.at("vocalic", default: false) + let vplace = spec.at("vplace", default: false) + let aperture = spec.at("aperture", default: false) + let lateral = spec.at("lateral", default: false) + // Feature-geometry model: "ch" (Clements & Hume) attaches [lateral] under the + // oral cavity (sibling of [continuant]); "sagey" attaches it under [coronal]. + let model = spec.at("model", default: "ch") + + // Normalize root to array + let root-feats = if type(root) == str { (root,) } else { root } + + // Auto-inference: show parent when any child is active + let radical = spec.at("radical", default: false) + + let laryngeal = laryngeal or spread or constricted or voice != false + + // [lateral] label and placement. In the Sagey model [lateral] hangs off + // [coronal]; in C&H it is a manner feature under the oral cavity. + let lateral-lbl = if lateral == true { "lateral" } else if lateral == "+" { "+lateral" } else if lateral == "-" { + "−lateral" + } else { none } + let lateral-cor = lateral-lbl != none and model == "sagey" // attach under [coronal] + let lateral-oc = lateral-lbl != none and model != "sagey" // attach under oral cavity + + // coronal may be bool or array; treat array as "active". + // A Sagey-style [lateral] forces [coronal] to appear as its host. + let coronal = if type(coronal) == array { coronal } else if ( + coronal or (anterior != false) or distributed or lateral-cor + ) { + true + } else { false } + let tense = spec.at("tense", default: false) + + // aperture is active when: true, or a non-empty array with at least one active element + let aperture-active = if type(aperture) == array { + aperture.any(v => v != false) + } else { aperture != false } + let vocalic = vocalic or vplace or aperture-active or (tense != false) + // If already in vocoid mode and place features are supplied, vplace is implied. + // labial/coronal/dorsal/radical may now be arrays (truthy) — != false covers all. + let place-active = (labial != false) or (coronal != false) or (dorsal != false) or (radical != false) + let vplace = vplace or (vocalic and place-active) + let vocalic = vocalic or vplace or aperture-active + let show-cplace = place-active or vocalic + + // [nasal] label + let nasal-lbl = if nasal == true { "nasal" } else if nasal == "+" { "+nasal" } else if nasal == "-" { + "−nasal" + } else { none } + + // [continuant] labels — accepts a single value or an array of up to 2 (affricates). + let _cont-lbl(v) = if v == true { "continuant" } else if v == "+" { "+continuant" } else if v == "-" { + "−continuant" + } else { none } + let continuant-lbls = if type(continuant) == array { + continuant.slice(0, calc.min(continuant.len(), 2)).map(_cont-lbl).filter(l => l != none) + } else { + let l = _cont-lbl(continuant) + if l != none { (l,) } else { () } + } + + // [voice] label + let voice-lbl = if voice == true { "voice" } else if voice == "+" { "+voice" } else if voice == "-" { + "−voice" + } else { none } + + // [anterior] label + let ant-lbl = if anterior == true { "anterior" } else if anterior == "+" { "+anterior" } else if anterior == "-" { + "−anterior" + } else { none } + + // [coronal] subtree — used when coronal is bool (existing anterior/distributed params) + let cor-ch-default = () + if ant-lbl != none { cor-ch-default = cor-ch-default + (_feat(ant-lbl),) } + if distributed { cor-ch-default = cor-ch-default + (_feat("distributed"),) } + + // Place features (shared by consonant and vocoid branches). + // Each of labial/coronal/dorsal/radical may be: + // false → node absent + // true → node shown, no sub-features (bool mode) + // array → node shown with sub-feature children from the array + // (e.g. dorsal: ("+high", "-back"), labial: ("round",)) + // Array mode for coronal replaces the anterior/distributed params. + let place-ch = () + if labial != false { + let ch = if type(labial) == array { labial.map(f => _feat(_norm-feat(f))) } else { () } + place-ch = place-ch + (_feat("labial", ch: ch),) + } + if coronal != false { + let ch = if type(coronal) == array { coronal.map(f => _feat(_norm-feat(f))) } else { cor-ch-default } + // Sagey model: [lateral] is a dependent of [coronal]. + if lateral-cor { ch = ch + (_feat(lateral-lbl),) } + place-ch = place-ch + (_feat("coronal", ch: ch),) + } + if radical != false { place-ch = place-ch + (_feat("radical"),) } + if dorsal != false { + let ch = if type(dorsal) == array { dorsal.map(f => _feat(_norm-feat(f))) } else { () } + place-ch = place-ch + (_feat("dorsal", ch: ch),) + } + + // [tense] label + let tense-lbl = if tense == true { "tense" } else if tense == "+" { "+tense" } else if tense == "-" { + "−tense" + } else { none } + + // Vocoid branch + let voc-ch = () + if tense-lbl != none { voc-ch = voc-ch + (_feat(tense-lbl),) } + if vplace { + voc-ch = voc-ch + (_class("V-place", place-ch),) + } + if aperture-active { + let ap-ch = if type(aperture) == array { + let names = ("open1", "open2", "open3") + let result = () + for (i, v) in aperture.slice(0, calc.min(aperture.len(), 3)).enumerate() { + let lbl = if v == true { names.at(i) } else if v == "+" { "+" + names.at(i) } else if v == "-" { + "−" + names.at(i) + } else { none } + if lbl != none { result = result + (_feat(lbl),) } + } + result + } else { () } + voc-ch = voc-ch + (_class("aperture", ap-ch),) + } + + // C-place children + let cplace-ch = if vocalic { (_class("vocalic", voc-ch),) } else { place-ch } + + // Auto-inference: != false covers true, "+", "-" + let show-oc = continuant-lbls.len() > 0 or show-cplace or lateral-oc + + // Oral cavity children. C&H model: [lateral] is a manner feature here, + // sitting alongside [continuant] and before C-place. + let oc-ch = continuant-lbls.map(_feat) + if lateral-oc { oc-ch = oc-ch + (_feat(lateral-lbl),) } + if show-cplace { oc-ch = oc-ch + (_class("C-place", cplace-ch),) } + + // Laryngeal children — [voice] is under laryngeal (Clements & Hume 1995) + let laryng-ch = () + if voice-lbl != none { laryng-ch = laryng-ch + (_feat(voice-lbl),) } + if spread { laryng-ch = laryng-ch + (_feat("spread"),) } + if constricted { laryng-ch = laryng-ch + (_feat("constricted"),) } + + // Root children + let root-ch = () + if laryngeal { root-ch = root-ch + (_class("laryngeal", laryng-ch),) } + if nasal-lbl != none { root-ch = root-ch + (_feat(nasal-lbl),) } + if show-oc { root-ch = root-ch + (_class("oral cavity", oc-ch),) } + + ( + tree: (label: "root", kind: "root", features: root-feats, children: root-ch), + is-vocoid: vocalic, + ) +} + +// ── Vocoid positional nudge ────────────────────────────────────────────────── +// Vocoid trees are naturally skewed: the deep vocalic subtree inflates the +// oral-cavity subtree's width, pushing it far right and laryngeal far left. +// Corrections: +// 1. Shift oral-cavity subtree left (oc-shift) +// 2. Shift laryngeal subtree right (lar-shift) +// 3. Individual nudges for [cont], [lab], [dor], [nasal] +// +// Subtrees identified via a single forward pass (pre-order). par coords are +// exact copies of the parent's (x,y), so array.contains() is exact. +#let _apply-vocoid-nudge(nodes, is-vocoid) = { + if not is-vocoid { return nodes } + + let oc-shift = -2.00 // move oral-cavity subtree left + let lar-shift = +1.10 // move laryngeal subtree right + + // Build a subtree membership set from a named root label. + let _build-sub(root-label) = { + let sub = () + let rn = nodes.find(e => e.label == root-label) + if rn != none { + sub = sub + ((rn.x, rn.y),) + for n in nodes { + if n.par != none and sub.contains(n.par) { + let pos = (n.x, n.y) + if not sub.contains(pos) { sub = sub + (pos,) } + } + } + } + sub + } + + let vp-extra = -0.35 // push V-place subtree left (more gap from aperture) + let ap-extra = +0.35 // push aperture subtree right (more gap from V-place) + // Only spread V-place/aperture when BOTH are present under vocalic; + // if only one is present it is the sole child and should stay centred. + + let oc-sub = _build-sub("oral cavity") + let lar-sub = _build-sub("laryngeal") + let vplace-sub = _build-sub("V-place") + let apt-sub = _build-sub("aperture") + + // Only apply root-level shifts when the root has multiple children. + let has-laryngeal = nodes.any(e => e.label == "laryngeal") + let has-nasal = nodes.any(e => e.label == "nasal" or e.label.ends-with("nasal")) + let has-open3 = nodes.any(e => e.label.ends-with("open3")) + let has-lab = nodes.any(e => e.label == "labial") + let has-cor = nodes.any(e => e.label == "coronal") + let has-dor = nodes.any(e => e.label == "dorsal") + // Full vocoid tree (laryngeal + nasal + OC): OC moves right to spread out. + // Partial tree (only laryngeal or only nasal): OC moves left as before. + let effective-oc-shift = if has-laryngeal and has-nasal { +0.10 } else if has-laryngeal or has-nasal { + oc-shift + } else { 0 } + let effective-lar-shift = if oc-sub.len() > 0 { lar-shift + (if has-open3 { 0.60 } else { 0 }) } else { 0 } + let effective-vp-extra = if vplace-sub.len() > 0 and apt-sub.len() > 0 { vp-extra } else { 0 } + let effective-ap-extra = if vplace-sub.len() > 0 and apt-sub.len() > 0 { ap-extra } else { 0 } + + // Total x displacement for a given (x,y) position — sum of all applicable shifts. + let _dx-for(pos) = { + let d = 0.0 + if oc-sub.contains(pos) { d = d + effective-oc-shift } + if vplace-sub.contains(pos) { d = d + effective-vp-extra } + if apt-sub.contains(pos) { d = d + effective-ap-extra } + if lar-sub.contains(pos) { d = d + effective-lar-shift } + d + } + + nodes.map(e => { + let base-x = e.x + _dx-for((e.x, e.y)) + let new-par = if e.par == none { none } else { + let pd = _dx-for(e.par) + if pd != 0 { (e.par.at(0) + pd, e.par.at(1)) } else { e.par } + } + + if e.label.ends-with("continuant") { + let cont-nudge = if not has-lab and has-cor and has-dor { -0.40 } else { 0 } + (..e, x: base-x + 2.80 + cont-nudge, par: new-par) + } else if e.label == "labial" { + // Only nudge toward [cor] when [cor] is actually present. + // Amount increases when [dor] is also there (three-way spread needs more room). + let lab-nudge = if has-cor { (if has-dor { 0.40 } else { 0.10 }) } else { 0 } + (..e, x: base-x + lab-nudge, par: new-par) + } else if e.label == "dorsal" { + if has-cor { + // [cor] is in the middle: pull [dor] left toward it and lift above open1. + (..e, x: base-x - 0.15, y: e.y + _stagger-dy, par: new-par) + } else { + // No [cor]: stay at natural position (sole feature, or alongside [lab] only). + (..e, x: base-x, par: new-par) + } + } else if e.label == "nasal" or e.label.ends-with("nasal") { + // Full vocoid tree only (has laryngeal): nudge nasal right. + let nx = if has-laryngeal { e.x + 0.90 + (if has-open3 { 0.60 } else { 0 }) } else { e.x } + (..e, x: nx, y: e.y - 0.30) + } else { + (..e, x: base-x, par: new-par) + } + }) +} + +// ── Node name for CeTZ anchor ──────────────────────────────────────────────── +// Based on the argument name: sign stripped, spaces→hyphens, NO abbreviation. +// "+anterior" → "anterior1", "oral cavity" → "oral-cavity2", "−voice" → "voice1" +#let _node-name(label, tidx) = { + let (_, base) = _sign-base(lower(label)) + base.replace(" ", "-") + str(tidx) +} + +// ── Manual position adjustments ────────────────────────────────────────────── +// `position` is an array of (key, dx, dy) triples. `key` is: +// - geom(): bare argument name, e.g. "continuant", "oral-cavity" +// - geom-group(): argument name + tree index, e.g. "continuant1", "oral-cavity2" +// `use-tidx`: when true, match key against _node-name(label, tidx); +// when false, match against bare base label (spaces→hyphens, no index). +// Moving a node also patches every node whose stored parent coords match the +// original position, so tree lines stay connected. +#let _apply-positions(nodes, position, use-tidx) = { + // Auto-wrap a single flat (key, dx, dy) triple + let position = if position.len() > 0 and type(position.at(0)) == str { + (position,) + } else { position } + if position.len() == 0 { return nodes } + + // Build adjustment dict: key → (dx, dy) + let adj = (:) + for entry in position { + adj.insert(entry.at(0), (entry.at(1), entry.at(2))) + } + + // Resolve key for a node entry + let node-key(e) = if use-tidx { + _node-name(e.label, e.tidx) + } else { + let (_, base) = _sign-base(lower(e.label)) + base.replace(" ", "-") + } + + // First pass: build "orig-x,orig-y" → new-(x,y) for all nodes that move, + // so we can patch children's stored parent coordinates in the second pass. + let moved = (:) + for e in nodes { + let key = node-key(e) + if key in adj { + let (dx, dy) = adj.at(key) + moved.insert(str(e.x) + "," + str(e.y), (e.x + dx, e.y + dy)) + } + } + + // Second pass: update node positions and parent references + nodes.map(e => { + let key = node-key(e) + let (nx, ny) = if key in adj { + let (dx, dy) = adj.at(key) + (e.x + dx, e.y + dy) + } else { (e.x, e.y) } + + let new-par = if e.par != none { + let pk = str(e.par.at(0)) + "," + str(e.par.at(1)) + if pk in moved { moved.at(pk) } else { e.par } + } else { none } + + (..e, x: nx, y: ny, par: new-par) + }) +} + +// ── Segment label normalisation ────────────────────────────────────────────── +// Strings are passed through ipa-to-unicode (TIPA conventions); content is used as-is. +// Strip phonemic/phonetic delimiters from a ph key so it can be looked up in _presets. +// "/a/" → "a", "[a]" → "a", "a" → "a" +#let _ph-bare(s) = if type(s) != str { s } else if s.starts-with("/") and s.ends-with("/") and s.len() > 2 { + s.slice(1, -1) +} else if s.starts-with("[") and s.ends-with("]") and s.len() > 2 { s.slice(1, -1) } else { s } + +// Render a segment label string. The user controls brackets by the string itself: +// "/a/" → /ipa("a")/ (phonemic) +// "[a]" → [ipa("a")] (phonetic) +// "a" → ipa("a") (bare) +// content → passed through unchanged +#let _seg(s) = if type(s) != str { s } else if s.starts-with("/") and s.ends-with("/") and s.len() > 2 { + [/#(ipa-to-unicode(s.slice(1, -1)))/] +} else if s.starts-with("[") and s.ends-with("]") and s.len() > 2 { + [\[#(ipa-to-unicode(s.slice(1, -1)))\]] +} else { ipa-to-unicode(s) } + +// ── Delink mark drawing ─────────────────────────────────────────────────────── +// Draws two parallel bars perpendicular to the parent→child line at its midpoint, +// matching the same symbol used in autoseg() / multi-tier(). +// (fx,fy) = parent bottom endpoint; (tx,ty) = child top endpoint. +#let _draw-delink(fx, fy, tx, ty, sw) = { + let dx = tx - fx + let dy = ty - fy + let len = calc.sqrt(dx * dx + dy * dy) + if len == 0 { return } + let dir-x = dx / len + let dir-y = dy / len + let perp-x = -dir-y + let perp-y = dir-x + let mid-x = (fx + tx) / 2 + let mid-y = (fy + ty) / 2 + let bar = 0.15 // half-length of each bar (canvas units) + let gap = 0.03 // half-gap between the two bars + for sign in (-1, 1) { + let cx = mid-x + sign * gap * dir-x + let cy = mid-y + sign * gap * dir-y + cetz.draw.line( + (cx - bar * perp-x, cy - bar * perp-y), + (cx + bar * perp-x, cy + bar * perp-y), + stroke: sw, + ) + } +} + +// ── General post-layout nudge (all tree types) ─────────────────────────────── +// When [voice] and [continuant] coexist they end up at nearly the same vertical +// level and close in x, causing overlap. Push [voice] down unconditionally. +#let _apply-general-nudge(nodes) = { + let has-cont = nodes.any(e => e.label.ends-with("continuant")) + let has-dor = nodes.any(e => e.label == "dorsal") + + // Full consonant tree: root has laryngeal + nasal + oral cavity all present. + // Shift the oral-cavity subtree slightly left so it doesn't crowd the tree. + // Gated on all three being present — leaves other consonant trees unchanged. + let has-lar = nodes.any(e => e.label == "laryngeal") + let has-nas = nodes.any(e => e.label == "nasal" or e.label.ends-with("nasal")) + let has-oc = nodes.any(e => e.label == "oral cavity") + let has-distr = nodes.any(e => e.label == "distributed") + let oc-shift = if has-lar and has-nas and has-oc { -0.50 } else { 0.0 } + + // Build oral-cavity subtree membership (pre-order, using original positions). + let oc-sub = if oc-shift != 0.0 { + let sub = () + let rn = nodes.find(e => e.label == "oral cavity") + if rn != none { + sub = sub + ((rn.x, rn.y),) + for n in nodes { + if n.par != none and sub.contains(n.par) { + let pos = (n.x, n.y) + if not sub.contains(pos) { sub = sub + (pos,) } + } + } + } + sub + } else { () } + + // Fix 2 — OC not centered under root when nasal+OC present but no laryngeal. + // Compute how far the root is displaced from the OC's current center. + let oc-center-shift = if not has-lar and has-nas and has-oc { + let rn = nodes.find(e => e.kind == "root") + let on = nodes.find(e => e.label == "oral cavity") + if rn != none and on != none { rn.x - on.x } else { 0.0 } + } else { 0.0 } + + // Build OC subtree membership for the center-shift (different gate from oc-sub). + let oc-center-sub = if oc-center-shift != 0.0 { + let sub = () + let on = nodes.find(e => e.label == "oral cavity") + if on != none { + sub = sub + ((on.x, on.y),) + for n in nodes { + if n.par != none and sub.contains(n.par) { + let pos = (n.x, n.y) + if not sub.contains(pos) { sub = sub + (pos,) } + } + } + } + sub + } else { () } + + // Fix 3 — [cor] and [rad] overlap when lab+cor+rad+dor all present. + // Pre-account for the rad nudge (-0.30 when dor present) and shift [cor] + // subtree to the midpoint between [lab].x and post-nudge [rad].x. + let rad-entry = nodes.find(e => e.label == "radical") + let lab-entry = nodes.find(e => e.label == "labial") + let cor-entry = nodes.find(e => e.label == "coronal") + let has-rad = rad-entry != none + + let cor-shift = if has-rad and cor-entry != none and lab-entry != none { + let rad-x = rad-entry.x + (if has-dor { -0.30 } else { 0.0 }) + (lab-entry.x + rad-x) / 2.0 - cor-entry.x + } else { 0.0 } + + let cor-sub = if cor-shift != 0.0 { + let sub = () + if cor-entry != none { + sub = sub + ((cor-entry.x, cor-entry.y),) + for n in nodes { + if n.par != none and sub.contains(n.par) { + let pos = (n.x, n.y) + if not sub.contains(pos) { sub = sub + (pos,) } + } + } + } + sub + } else { () } + + // Fix 4 — second [−cont] overlaps C-place in affricates (two continuant nodes). + let cont-nodes = nodes.filter(e => e.label.ends-with("continuant")) + let cont-count = cont-nodes.len() + let cont-right-x = if cont-count == 2 { + calc.max(..cont-nodes.map(e => e.x)) + } else { none } + + // Fix 5 — C&H [lateral] leaf is a sibling of [continuant] under oral cavity and + // its label crowds the C-place node. Locate the oral-cavity node so a lateral + // child of it can be nudged clear. (Sagey laterals sit under [coronal], untouched.) + let oc-lat-host = nodes.find(e => e.label == "oral cavity") + + // Fix 6 — a Sagey [lateral] is a child of [coronal], which widens the C-place + // subtree and pushes C-place right, stranding [+cont] far to the left under + // oral cavity. Detect that case so [+cont] can be nudged right to recentre it. + let cor-lat-host = nodes.find(e => e.label == "coronal") + let has-sagey-lat = cor-lat-host != none and nodes.any(e => ( + e.label.ends-with("lateral") and e.par == (cor-lat-host.x, cor-lat-host.y) + )) + + nodes.map(e => { + // [voice] drops down when [continuant] is also present (avoids overlap). + let e2 = if has-cont and (e.label == "voice" or e.label.ends-with("voice")) { + (..e, y: e.y - 0.80) + } else { e } + // Full tree: nudge [nasal] left and up so it sits naturally between laryngeal and oral cavity. + let e2 = if oc-shift != 0.0 and (e2.label == "nasal" or e2.label.ends-with("nasal")) { + (..e2, x: e2.x + 0.10, y: e2.y + 0.20) + } else { e2 } + // Fix 7: retroflex nasal ɳ has a wide oral-cavity subtree ([distr] under + // [cor]) that crowds [+nasal] against the laryngeal line — shift it right. + // Gated like the nudge above (oc-shift != 0 ⇒ full laryngeal+nasal+oral-cavity + // tree) plus [distr], so it only fires when that laryngeal line is present. + // Among the presets this is unique to ɳ; a hand-built nasal retroflex with a + // laryngeal hits the same crowding and benefits from the same shift. + let e2 = if oc-shift != 0.0 and has-distr and (e2.label == "nasal" or e2.label.ends-with("nasal")) { + (..e2, x: e2.x + 0.35) + } else { e2 } + // [rad] nudge left when [dor] is also present, to close the gap between them. + let e2 = if has-dor and e2.label == "radical" { + (..e2, x: e2.x - 0.30) + } else { e2 } + // Fix 5: C&H [lateral] child of oral cavity overlaps C-place — push left+down. + let e2 = if e2.label.ends-with("lateral") and oc-lat-host != none and e.par == ( + oc-lat-host.x, + oc-lat-host.y, + ) { + (..e2, x: e2.x - 0.35, y: e2.y - 0.30) + } else { e2 } + // Fix 6: Sagey [lateral] widens C-place — nudge [+cont] right under oral cavity. + let e2 = if has-sagey-lat and e2.label.ends-with("continuant") and oc-lat-host != none and e.par == ( + oc-lat-host.x, + oc-lat-host.y, + ) { + (..e2, x: e2.x + 0.45) + } else { e2 } + // Fix 3: shift [cor] subtree to midpoint between [lab] and post-nudge [rad]. + let e2 = if cor-sub.contains((e.x, e.y)) { + let new-par = if e2.par == none { none } else if cor-sub.contains(e2.par) { + (e2.par.at(0) + cor-shift, e2.par.at(1)) + } else { e2.par } + (..e2, x: e2.x + cor-shift, par: new-par) + } else { e2 } + // Fix 4: nudge the rightmost continuant node left+down when two are present. + let e2 = if cont-count == 2 and e2.label.ends-with("continuant") and e2.x == cont-right-x { + (..e2, x: e2.x - 0.25, y: e2.y - 0.20) + } else { e2 } + // Shift oral-cavity subtree left in full consonant trees (has-lar+has-nas+has-oc). + let e2 = if oc-sub.contains((e.x, e.y)) { + let new-par = if e2.par == none { none } else if oc-sub.contains(e2.par) { + (e2.par.at(0) + oc-shift, e2.par.at(1)) + } else { e2.par } + (..e2, x: e2.x + oc-shift, par: new-par) + } else { e2 } + // Fix 2: center OC subtree under root when nasal+OC present but no laryngeal. + if oc-center-sub.contains((e.x, e.y)) { + let new-par = if e2.par == none { none } else if oc-center-sub.contains(e2.par) { + (e2.par.at(0) + oc-center-shift, e2.par.at(1)) + } else { e2.par } + (..e2, x: e2.x + oc-center-shift, par: new-par) + } else { e2 } + }) +} + +/// Draw a feature-geometry tree for a consonant or vocoid. +/// +/// Arguments control which nodes are present. By default all nodes are absent. +/// Parent nodes are inferred automatically from their children (e.g. specifying +/// `spread: true` automatically shows "laryngeal"). +/// +/// - root (array): Feature strings for the root matrix, e.g. `("+son", "-vocoid")`. +/// Accepts the same formats as `feat()`. +/// - laryngeal (bool): Show "laryngeal" class node. +/// - nasal (bool, str): Show `[nasal]`. Pass `true` → `[nasal]`, +/// `"+"` → `[+nasal]`, `"-"` → `[−nasal]`. +/// - spread (bool): Show `[spread]` under laryngeal. +/// - constricted (bool): Show `[constricted]` under laryngeal. +/// - voice (bool, str): Show `[voice]` under laryngeal (Clements & Hume 1995). Pass +/// `true` → `[voice]`, `"+"` → `[+voice]`, `"-"` → `[−voice]`. +/// - continuant (bool, str): Show `[continuant]` under oral cavity. Pass `true` → `[cont]`, +/// `"+"` → `[+cont]`, `"-"` → `[−cont]`. +/// - labial (bool): Show `[labial]` (under C-place or V-place). +/// - coronal (bool): Show `[coronal]` (under C-place or V-place). +/// - anterior (bool, str): Show `[anterior]`. Pass `true` → `[anterior]`, +/// `"+"` → `[+anterior]`, `"-"` → `[−anterior]`. +/// - distributed (bool): Show `[distributed]` under `[coronal]`. +/// - radical (bool): Show `[rad]` (radical/pharyngeal) under C-place or V-place. +/// - dorsal (bool, array): Show `[dorsal]` (under C-place or V-place). +/// Pass an array of feature strings to add sub-features (Sagey-style): +/// `dorsal: ("+high", "-back")` → `[dor]` with `[+hi]` and `[−back]` children. +/// - labial (bool, array): Show `[labial]`. Array adds sub-features: +/// `labial: ("round",)` → `[lab]` with `[round]` child. +/// - coronal (bool, array): Show `[coronal]`. Array provides children directly, +/// replacing the separate `anterior`/`distributed` params: +/// `coronal: ("+ant", "-distr")` → `[cor]` with `[+ant]` and `[−distr]`. +/// - tense (bool, str): Show `[tense]` under the vocalic node. Pass `true` → `[tense]`, +/// `"+"` → `[+tense]`, `"-"` → `[−tense]`. Automatically infers `vocalic: true`. +/// Used in Sagey-style representations to distinguish [e]/[ɛ] and [o]/[ɔ]. +/// - vocalic (bool): Show "vocalic" class node under C-place (vocoid branch). +/// - vplace (bool): Show "V-place" under vocalic. When true, `labial`/`coronal`/`dorsal` +/// attach here instead of directly under C-place. Inferred automatically when +/// `vocalic` is active and any place feature is supplied. +/// - aperture (bool, array): Show "aperture" class node under vocalic. +/// Pass `true` → node only; pass an array of up to 3 values to show +/// `[open1]`/`[open2]`/`[open3]` as children. Each element may be +/// `true` → `[openN]`, `"+"` → `[+openN]`, `"-"` → `[−openN]`, +/// or `false` → omit that degree. E.g. `aperture: ("+", false, "-")`. +/// (Replaces the former `open` parameter.) +/// - lateral (bool, str): Show `[lateral]`. Pass `true` → `[lateral]`, +/// `"+"` → `[+lateral]`, `"-"` → `[−lateral]`. Attachment depends on `model`: +/// under the oral cavity (sibling of `[continuant]`) for `"ch"`, under +/// `[coronal]` for `"sagey"`. In the Sagey case `[coronal]` is shown automatically. +/// - scale (number): Uniform scale factor (default: 1). +/// - position (array): Manual position tweaks. Each entry: `(key, dx, dy)` where +/// `key` is the bare argument name (`"continuant"`, `"oral-cavity"`) and `dx`/`dy` +/// are canvas-unit offsets (positive x = right, positive y = up). +/// Example: `position: (("continuant", -0.2, 0.3),)` +/// - delinks (array): Node keys whose line *to their parent* is replaced with a +/// delink mark (two perpendicular bars). Keys follow the same convention as +/// `position`: bare argument name for `geom()`, e.g. `delinks: ("c-place",)`. +/// - prefix (str): String prepended to the segment label. `"-"` is automatically converted to `"–"`. E.g. `prefix: "-"` → `–/a/`. +/// - suffix (str): String appended to the segment label. `"-"` is automatically converted to `"–"`. +/// - segment (content): Optional label centred above the root node, e.g. `"s"` or `$s$`. +/// - ph (str): Pre-built segment preset. Supports `"a"`, `"e"`, `"i"`, `"o"`, `"u"`, +/// `"E"` (ɛ), `"O"` (ɔ). The segment label defaults to the `ph` value unless overridden +/// by `segment`. Any other explicitly-provided argument overrides the corresponding preset +/// value: `#geom(ph: "O", root: ("+son", "+approx"))` replaces its root features. +/// Example: `#geom(ph: "i", scale: 1.5)`. +/// - model (str): Feature-geometry model for preset vowels. `"ch"` (default) uses +/// Clements & Hume 1995 (aperture nodes for height). `"sagey"` uses Sagey 1986 +/// (dorsal sub-features for height/backness, labial `[round]` for rounding, no aperture). +/// Consonant presets are otherwise identical in both models, except for the +/// placement of `[lateral]` (oral cavity under `"ch"`, `[coronal]` under `"sagey"`). +/// -> content +#let geom( + ph: none, + ui-lang: "en", + model: "ch", + root: (), + laryngeal: false, + nasal: false, + spread: false, + constricted: false, + voice: false, + continuant: false, + labial: false, + coronal: false, + anterior: false, + distributed: false, + dorsal: false, + radical: false, + vocalic: false, + vplace: false, + aperture: false, + tense: false, + lateral: false, + scale: 1.0, + position: (), + delinks: (), + segment: none, + prefix: "", + suffix: "", + highlight: (), + timing: auto, +) = { + let ui-locale = resolve-ui-lang(ui-lang) + if ui-locale == none { + return ui-lang-error(ui-lang) + } + + // Auto-detect length from ph: "iː" or "i:" → long (two timing slots). + // Only a TRAILING colon counts as a length mark — a colon elsewhere belongs to + // a TIPA escape such as "\:t" (ʈ) and must not be stripped, or the preset lookup + // would fail. Strip just the trailing mark; keep the original as label fallback. + let _is-long = ph != none and type(ph) == str and (ph.ends-with("ː") or ph.ends-with(":")) + let _ph-orig = ph + let ph = if _is-long { ph.trim("ː", at: end, repeat: false).trim(":", at: end, repeat: false) } else { ph } + + // Resolve timing: + // auto → one × normally, two × when ph contains a length mark + // false → no timing tier + // string/symbol/array → explicit (normalized below) + let timing = if timing == false { + () + } else if timing == auto { + if _is-long { ($times$, $times$) } else { ($times$,) } + } else { + // Coerce bare string/symbol to array, then normalize "mora"/"mu" → μ, "x"/"X" → × + let t = if type(timing) == str or type(timing) == symbol { (timing,) } else { timing } + t.map(t => if type(t) == str { + let tl = lower(t) + if tl == "mora" or tl == "mu" { sym.mu } else if tl == "x" { $times$ } else { t } + } else { t }) + } + let prefix = prefix.replace("-", "–") + let suffix = suffix.replace("-", "–") + let scale-factor = scale + + // When ph is set, load the preset, then apply any explicitly-provided + // non-default arguments on top. This lets callers override individual keys: + // #geom(ph: "O", root: ("+son", "+approx")) ← root replaces preset's root + // Sagey model uses _presets-sagey for vowels; consonants fall back to _presets. + let ph-key = if ph != none { _ph-bare(ph) } else { none } + let spec = if ph-key != none and ph-key in _presets { + let preset-dict = if model == "sagey" and ph-key in _presets-sagey { + _presets-sagey + } else { _presets } + // Use segment label exactly as provided by the user; fall back to the preset's own segment field. + let seg = if segment != none { prefix + segment + suffix } else { prefix + _ph-orig + suffix } + let overrides = (:) + if root != () { overrides.insert("root", root) } + if laryngeal != false { overrides.insert("laryngeal", laryngeal) } + if nasal != false { overrides.insert("nasal", nasal) } + if spread != false { overrides.insert("spread", spread) } + if constricted != false { overrides.insert("constricted", constricted) } + if voice != false { overrides.insert("voice", voice) } + if continuant != false { overrides.insert("continuant", continuant) } + if labial != false { overrides.insert("labial", labial) } + if coronal != false { overrides.insert("coronal", coronal) } + if anterior != false { overrides.insert("anterior", anterior) } + if distributed != false { overrides.insert("distributed", distributed) } + if dorsal != false { overrides.insert("dorsal", dorsal) } + if radical != false { overrides.insert("radical", radical) } + if vocalic != false { overrides.insert("vocalic", vocalic) } + if vplace != false { overrides.insert("vplace", vplace) } + if aperture != false { overrides.insert("aperture", aperture) } + if tense != false { overrides.insert("tense", tense) } + if lateral != false { overrides.insert("lateral", lateral) } + (..(preset-dict.at(ph-key)), segment: seg, ..overrides) + } else { + ( + root: root, + laryngeal: laryngeal, + nasal: nasal, + spread: spread, + constricted: constricted, + voice: voice, + continuant: continuant, + labial: labial, + coronal: coronal, + anterior: anterior, + distributed: distributed, + dorsal: dorsal, + radical: radical, + vocalic: vocalic, + vplace: vplace, + aperture: aperture, + tense: tense, + lateral: lateral, + segment: if segment != none { prefix + segment + suffix } else if prefix != "" or suffix != "" { + prefix + suffix + } else { none }, + ) + } + let spec = (..spec, model: model) + let result = _build-tree(spec) + let tree = result.tree + let is-vocoid = result.is-vocoid + + let nodes = _layout(tree, 0.0, 0.0, none) + let nodes = _apply-vocoid-nudge(nodes, is-vocoid) + let nodes = _apply-general-nudge(nodes) + // Tag with tree index 1 (single tree) + let nodes = nodes.map(e => (..e, tidx: 1)) + let nodes = _apply-positions(nodes, position, false) + + // ── Render ──────────────────────────────────────────────────────────── + let _loff = 0.20 + let _dim = luma(65%) + let _norm = luma(15%) + // Per-node color: dim everything not in the highlight set (when set is non-empty). + let _nc = nname => if highlight.len() == 0 or nname in highlight { _norm } else { _dim } + + // Dynamic baseline: anchor root node (canvas y=0) at the text baseline. + let y-min = nodes.fold(0.0, (acc, e) => calc.min(acc, e.y)) + + context { + let em-in-cu = text.size / (scale-factor * 1cm) + let _timing-gap = 0.65 // vertical gap from root to timing tier + let _timing-y = 0.55 + _timing-gap // y-coordinate of timing nodes (root at 0) + let _t-spacing = 0.55 // horizontal gap between timing nodes + let seg-present = spec.at("segment", default: none) != none + let y-top = if timing.len() > 0 { + // timing nodes sit at _timing-y; segment label (if any) floats above them + let label-top = if seg-present { + _timing-y + 0.45 + text.size * 0.84 / (scale-factor * 1cm) + } else { + _timing-y + text.size * 0.35 / (scale-factor * 1cm) + } + label-top + } else if seg-present { + 0.55 + text.size * 0.84 / (scale-factor * 1cm) + } else { + text.size * 0.35 / (scale-factor * 1cm) + } + let bl = (em-in-cu + (-y-min)) / (2 * em-in-cu + y-top - y-min) + let fsz = text.size * 0.70 * scale-factor + let font = phonokit-font.get() + + box(inset: 1em * scale-factor, baseline: bl * 100%, { + cetz.canvas(length: scale-factor * 1cm, { + import cetz.draw: * + + for entry in nodes { + let nname = _node-name(entry.label, entry.tidx) + let is-delinked = nname.slice(0, -1) in delinks + let nc = _nc(nname) + + if entry.par != none { + let (px, py) = entry.par + let (fx, fy) = (px, py - _loff) + let (tx, ty) = (entry.x, entry.y + _loff) + let par-entry = nodes.find(e => e.x == px and e.y == py) + let par-nname = if par-entry != none { _node-name(par-entry.label, par-entry.tidx) } else { none } + let both-highlighted = ( + highlight.len() > 0 and nname in highlight and par-nname != none and par-nname in highlight + ) + let line-paint = if highlight.len() == 0 or both-highlighted { _norm } else { _dim } + let sw = (paint: line-paint, thickness: 0.016) + line((fx, fy), (tx, ty), stroke: sw) + if is-delinked { _draw-delink(fx, fy, tx, ty, sw) } + } + + if entry.kind == "root" { + content( + (entry.x, entry.y), + text(font: font, size: fsz, fill: nc, ui-geom-label("root", ui-locale)), + name: nname, + ) + if entry.feats.len() > 0 { + let mat-x = entry.x + 0.25 + let items = entry.feats.map(f => { + let norm = f.replace("-", "−") + let (sign, base) = if norm.starts-with("±") { ("±", norm.slice("±".len())) } else if norm.starts-with( + "+", + ) { ("+", norm.slice(1)) } else if norm.starts-with("−") { ("−", norm.slice("−".len())) } else { + ("", norm) + } + text(font: font, fill: nc, if sign != "" { box(width: 0.65em, align(center, sign)) + base } else { + norm + }) + }) + content( + (mat-x, entry.y), + { + set text(font: font, size: fsz, fill: nc) + box(baseline: 50%, math.vec( + align: left, + delim: "[", + gap: 0pt, + ..items, + )) + }, + anchor: "west", + ) + } + } else { + let inner = if entry.kind == "feature" { + [\[#(_display(entry.label, ui-lang: ui-locale))\]] + } else { + [#(_display(entry.label, ui-lang: ui-locale))] + } + content( + (entry.x, entry.y), + text(font: font, size: fsz, fill: nc, inner), + name: nname, + ) + } + } + + // Segment label — always full colour (never dimmed by highlight) + let seg-label = spec.at("segment", default: none) + if seg-label != none { + let root-entry = nodes.find(e => e.kind == "root") + if root-entry != none { + let label-y = if timing.len() > 0 { + root-entry.y + _timing-y + 0.45 + } else { + root-entry.y + 0.55 + } + content( + (root-entry.x, label-y), + text(font: font, size: fsz * 1.2, fill: _norm, _seg(seg-label)), + anchor: "south", + ) + } + } + + // Timing tier (X-slots, morae, etc.) + if timing.len() > 0 { + let root-entry = nodes.find(e => e.kind == "root") + if root-entry != none { + let rx = root-entry.x + let ry = root-entry.y + let t-y = ry + _timing-y + let n = timing.len() + let t-paint = if highlight.len() == 0 { _norm } else { _dim } + for (i, t) in timing.enumerate() { + let tx = if n == 1 { rx } else { rx + (i - (n - 1) / 2.0) * _t-spacing } + line( + (rx, ry + _loff), + (tx, t-y - _loff), + stroke: (paint: t-paint, thickness: 0.016), + ) + content((tx, t-y), text(font: font, size: fsz, fill: t-paint, t)) + } + } + } + }) + }) + } // context +} + +/// Draw two or more feature-geometry trees side by side in a single canvas, +/// with optional dashed arrows connecting nodes across trees. +/// +/// Each tree is specified as a dict with the same keys as `geom()` (all +/// optional, same defaults). Trees cannot be passed as rendered `#geom()` +/// content — pass spec dicts or `#let` variables instead: +/// +/// ```typst +/// #let consonant = (root: ("-son",), labial: true) +/// #let vowel = (root: ("+son",), vocalic: true, dorsal: true) +/// #geom-group(consonant, vowel, +/// arrows: (("labial1", "dorsal2"),)) +/// ``` +/// +/// Node names are formed by stripping `+`/`−` prefixes, replacing spaces with +/// hyphens, and appending the 1-based tree index: +/// `"anterior1"`, `"oral-cavity2"`, `"c-place1"`, `"root2"`, etc. +/// +/// Each arrow entry is either a simple array `(from, to)` or a dict with +/// named keys for full control: +/// ```typst +/// arrows: ( +/// ("labial1", "labial2"), // simple +/// (from: "cor1", to: "cor2", color: blue), // coloured +/// (from: "dor1", to: "dor2", ctrl: (1.0, -0.5)), // custom S-curve +/// ) +/// ``` +/// +/// - ..trees (arguments): Positional spec dicts, one per tree. +/// - arrows (array): Cross-tree arrows. Each entry: `(from, to)` array or +/// `(from: str, to: str, color: color, ctrl: array)` dict (all keys except +/// `from`/`to` optional). +/// - gap (number): Canvas-unit gap between trees (default: 1.5). +/// - scale (number): Uniform scale factor (default: 1). +/// - model (str): Feature-geometry model for preset vowels. `"ch"` (default) or `"sagey"`. +/// Applies to all trees in the group. See `geom()` for details. +/// - position (array): Manual position tweaks after layout. Each entry: `(key, dx, dy)` +/// where `key` is the node anchor name with tree index (`"continuant1"`, `"oral-cavity2"`) +/// and `dx`/`dy` are canvas-unit offsets. Arrows automatically use the adjusted positions. +/// Example: `position: (("continuant1", -0.2, 0.3),)` +/// - delinks (array): Node anchor names (with tree index) whose line to their parent is +/// replaced with a delink mark. E.g. `delinks: ("c-place1",)`. +/// - curved (bool): When `true`, arrows are drawn as quadratic bézier curves with +/// automatic obstacle avoidance — they route around intervening nodes rather than +/// crossing them. Uses the same algorithm as `#vowels()`. (default: `false`) +/// -> content +#let geom-group( + ..args, + arrows: (), + gap: 1.5, + scale: 1.0, + ui-lang: "en", + model: "ch", + position: (), + delinks: (), + curved: false, + highlight: (), +) = { + let ui-locale = resolve-ui-lang(ui-lang) + if ui-locale == none { + return ui-lang-error(ui-lang) + } + + let specs = args.pos() + let scale-factor = scale + + // Auto-wrap a flat ("from", "to") pair so both forms are valid: + // arrows: ("labial1", "c-place3") ← single arrow, flat + // arrows: (("labial1", "c-place3"), ...) ← multiple arrows, nested + let arrows = if arrows.len() > 0 and type(arrows.at(0)) == str { + (arrows,) + } else { arrows } + + // ── Build and layout each tree, offset x by cumulative width + gap ──── + // Each spec may carry a `scale` key (default 1.0) that scales that tree's + // coordinates and font size relative to the group scale. + let all-nodes = () + let x-cursor = 0.0 + let seg-labels = () // (x, y, ts, text, root-nname, has-timing) + let timing-data = () // (root-x, ts, resolved-timing-array) + for (idx, spec) in specs.enumerate() { + // Auto-detect a TRAILING length mark in ph; strip just that so preset lookup + // works. A mid-string colon belongs to a TIPA escape like "\:t" (ʈ) — leave it. + let ph-raw = spec.at("ph", default: none) + let _is-long = ph-raw != none and type(ph-raw) == str and (ph-raw.ends-with("ː") or ph-raw.ends-with(":")) + let spec = if _is-long { + (..spec, ph: ph-raw.trim("ː", at: end, repeat: false).trim(":", at: end, repeat: false)) + } else { spec } + + // Resolve preset if ph key is present; explicit spec keys override the preset. + // Sagey model uses _presets-sagey for vowels; consonants fall back to _presets. + let spec = if "ph" in spec and _ph-bare(spec.at("ph")) in _presets { + let ph-val = spec.at("ph") + let ph-key = _ph-bare(ph-val) + let preset-dict = if model == "sagey" and ph-key in _presets-sagey { + _presets-sagey + } else { _presets } + let base = preset-dict.at(ph-key) + let px = spec.at("prefix", default: "").replace("-", "–") + let sx = spec.at("suffix", default: "").replace("-", "–") + // Use original ph (with length mark) as segment label fallback + let seg-ph = if ph-raw != none { ph-raw } else { ph-val } + let seg = if "segment" in spec { px + spec.at("segment") + sx } else { px + seg-ph + sx } + let overrides = (:) + for pair in spec.pairs() { + if pair.at(0) not in ("ph", "prefix", "suffix") { overrides.insert(pair.at(0), pair.at(1)) } + } + (..base, segment: seg, ..overrides) + } else { spec } + + // Resolve timing for this tree (same logic as geom()) + let timing-raw = spec.at("timing", default: auto) + let tree-timing = if timing-raw == false { + () + } else if timing-raw == auto { + if _is-long { ($times$, $times$) } else { ($times$,) } + } else { + let t = if type(timing-raw) == str or type(timing-raw) == symbol { (timing-raw,) } else { timing-raw } + t.map(t => if type(t) == str { + let tl = lower(t) + if tl == "mora" or tl == "mu" { sym.mu } else if tl == "x" { $times$ } else { t } + } else { t }) + } + + let spec = (..spec, model: model) + let result = _build-tree(spec) + let tree = result.tree + let is-vocoid = result.is-vocoid + let ts = spec.at("scale", default: 1.0) // per-tree relative scale + let px = spec.at("prefix", default: "").replace("-", "–") + let sx = spec.at("suffix", default: "").replace("-", "–") + let seg = if spec.at("segment", default: none) != none { px + spec.at("segment") + sx } else { none } + let nodes = _layout(tree, 0.0, 0.0, none) + let nodes = _apply-vocoid-nudge(nodes, is-vocoid) + let nodes = _apply-general-nudge(nodes) + let tidx = idx + 1 + // Scale coordinates and offset into the shared canvas. + all-nodes = ( + all-nodes + + nodes.map(e => ( + ..e, + x: e.x * ts + x-cursor, + y: e.y * ts, + par: if e.par == none { none } else { + (e.par.at(0) * ts + x-cursor, e.par.at(1) * ts) + }, + tidx: tidx, + tscale: ts, + )) + ) + let root-w = _tree-w(tree) + let root-x = x-cursor + root-w / 2 * ts + let root-nname = _node-name("root", tidx) + // Record segment label position + if seg != none { + seg-labels = seg-labels + ((root-x, 0.0, ts, seg, root-nname, tree-timing.len() > 0),) + } + // Record timing tier data + if tree-timing.len() > 0 { + timing-data = timing-data + ((root-x, ts, tree-timing),) + } + x-cursor = x-cursor + _tree-w(tree) * ts + gap + } + let all-nodes = _apply-positions(all-nodes, position, true) + + // ── Render ──────────────────────────────────────────────────────────── + let _loff = 0.20 + let _dim = luma(65%) + let _norm = luma(15%) + let _nc = nname => if highlight.len() == 0 or nname in highlight { _norm } else { _dim } + + let y-min-g = all-nodes.fold(0.0, (acc, e) => calc.min(acc, e.y)) + + // Build name → (x, y, loff) lookup OUTSIDE the canvas block. + // dict.insert() inside CeTZ's canvas block may not behave correctly + // because CeTZ processes drawing commands in a special context. + let name-to-pos = (:) + for e in all-nodes { + name-to-pos.insert(_node-name(e.label, e.tidx), (e.x, e.y, _loff * e.tscale)) + } + + context { + let em-in-cu-g = text.size / (scale-factor * 1cm) + let _timing-gap = 0.65 + let _timing-y = 0.55 + _timing-gap + let _t-spacing = 0.55 + // y-top-g: highest point across all trees (timing + segment labels) + let y-top-g = { + let timing-tops = timing-data.map(td => { + let (rx, ts, tt) = td + let has-seg = seg-labels.any(sl => calc.abs(sl.at(0) - rx) < 0.001) + if has-seg { + (_timing-y + 0.45 + text.size * 0.84 / (scale-factor * 1cm)) * ts + } else { + (_timing-y + text.size * 0.35 / (scale-factor * 1cm)) * ts + } + }) + let seg-tops = seg-labels + .filter(sl => not sl.at(5)) + .map(sl => 0.55 * sl.at(2) + text.size * 0.84 / (scale-factor * 1cm)) + let all-tops = timing-tops + seg-tops + if all-tops.len() > 0 { + all-tops.fold(text.size * 0.35 / (scale-factor * 1cm), calc.max) + } else { + text.size * 0.35 / (scale-factor * 1cm) + } + } + let bl-g = (em-in-cu-g + (-y-min-g)) / (2 * em-in-cu-g + y-top-g - y-min-g) + let fsz = text.size * 0.70 * scale-factor + let font = phonokit-font.get() + + box(inset: 1em * scale-factor, baseline: bl-g * 100%, { + cetz.canvas(length: scale-factor * 1cm, { + import cetz.draw: * + + // Draw all tree nodes + for entry in all-nodes { + let nname = _node-name(entry.label, entry.tidx) + let ts = entry.tscale + let efsz = fsz * ts + let eloff = _loff * ts + let nc = _nc(nname) + + if entry.par != none { + let (px, py) = entry.par + let (fx, fy) = (px, py - eloff) + let (tx, ty) = (entry.x, entry.y + eloff) + let par-entry = all-nodes.find(e => e.x == px and e.y == py) + let par-nname = if par-entry != none { _node-name(par-entry.label, par-entry.tidx) } else { none } + let both-highlighted = ( + highlight.len() > 0 and nname in highlight and par-nname != none and par-nname in highlight + ) + let line-paint = if highlight.len() == 0 or both-highlighted { _norm } else { _dim } + let sw = (paint: line-paint, thickness: 0.016) + line((fx, fy), (tx, ty), stroke: sw) + if nname in delinks { _draw-delink(fx, fy, tx, ty, sw) } + } + + if entry.kind == "root" { + content( + (entry.x, entry.y), + text(font: font, size: efsz, fill: nc, ui-geom-label("root", ui-locale)), + name: nname, + ) + if entry.feats.len() > 0 { + let mat-x = entry.x + 0.25 * ts + let items = entry.feats.map(f => { + let norm = f.replace("-", "−") + let (sign, base) = if norm.starts-with("±") { ("±", norm.slice("±".len())) } else if norm.starts-with( + "+", + ) { ("+", norm.slice(1)) } else if norm.starts-with("−") { ("−", norm.slice("−".len())) } else { + ("", norm) + } + text(font: font, fill: nc, if sign != "" { box(width: 0.65em, align(center, sign)) + base } else { + norm + }) + }) + content( + (mat-x, entry.y), + { + set text(font: font, size: efsz, fill: nc) + box(baseline: 50%, math.vec( + align: left, + delim: "[", + gap: 0pt, + ..items, + )) + }, + anchor: "west", + ) + } + } else { + let inner = if entry.kind == "feature" { + [\[#(_display(entry.label, ui-lang: ui-locale))\]] + } else { + [#(_display(entry.label, ui-lang: ui-locale))] + } + content( + (entry.x, entry.y), + text(font: font, size: efsz, fill: nc, inner), + name: nname, + ) + } + } + + // Segment labels — always full colour (never dimmed by highlight) + for (sx, sy, ts, seg, root-nname, has-timing) in seg-labels { + let label-y = sy + (if has-timing { (_timing-y + 0.45) * ts } else { 0.55 * ts }) + let seg-body = _seg(seg) + content( + (sx, label-y), + text(font: font, size: fsz * ts * 1.2, fill: _norm, seg-body), + anchor: "south", + ) + } + + // Timing tiers + let t-paint = if highlight.len() == 0 { _norm } else { _dim } + for (rx, ts, tt) in timing-data { + let ry = 0.0 + let t-y = ry + _timing-y * ts + let n = tt.len() + let loff = 0.20 * ts + for (i, t) in tt.enumerate() { + let tx = if n == 1 { rx } else { rx + (i - (n - 1) / 2.0) * _t-spacing * ts } + line( + (rx, ry + loff), + (tx, t-y - loff), + stroke: (paint: t-paint, thickness: 0.016), + ) + content((tx, t-y), text(font: font, size: fsz * ts, fill: t-paint, t)) + } + } + + // Draw cross-tree arrows. + // Shaft is dashed; head is a separate solid segment so the arrowhead + // contour is never dashed (same technique as #vowels). + // When curved: quadratic bézier with obstacle avoidance (same algorithm as vowels.typ). + let head-back = 0.12 // canvas units of solid segment before the tip + let clearance = 0.45 // obstacle avoidance radius (canvas units) + let sample-ts = (0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9) + let obs-pts = all-nodes.map(e => (e.x, e.y)) // all node centres + + for arrow in arrows { + // Accept both positional arrays ("from", "to") / ("from", "to", color) + // and dicts (from: "...", to: "...", color: ..., ctrl: ...). + let is-dict = type(arrow) == dictionary + let from-name = if is-dict { arrow.at("from") } else { arrow.at(0) } + let to-name = if is-dict { arrow.at("to") } else { arrow.at(1) } + let paint = if is-dict { arrow.at("color", default: luma(15%)) } else if arrow.len() >= 3 { + arrow.at(2) + } else { luma(15%) } + // ctrl: two-element array (lift1, lift2) — Y-offsets from each endpoint. + // When set, bypasses curved entirely. + let ctrl-val = if is-dict { arrow.at("ctrl", default: none) } else { none } + + if from-name in name-to-pos and to-name in name-to-pos { + let (fx, fy, f-loff) = name-to-pos.at(from-name) + let (tx, ty, t-loff) = name-to-pos.at(to-name) + // Dim arrow if neither endpoint is highlighted (when highlight is active). + let arrow-lit = highlight.len() == 0 or from-name in highlight or to-name in highlight + let paint = if arrow-lit { paint } else { _dim } + // Arrows connect to the TOP of each node (where parent lines terminate), + // EXCEPT for root nodes which have no parent — they connect at the BOTTOM + // (facing their children) to avoid landing near the segment label above. + let fy = if from-name.starts-with("root") { fy - f-loff } else { fy + f-loff } + let ty = ty - t-loff + let dx = tx - fx + let dy = ty - fy + let len = calc.sqrt(dx * dx + dy * dy) + + // ── Control points ───────────────────────────────────────────── + // Priority: ctrl > curved. + // ctrl: explicit Y-offsets from each endpoint → cubic Bézier, any shape. + // curved: auto S-curve scaled by distance. + let (ctrl1, ctrl2) = if ctrl-val != none { + // ctrl: (lift1, lift2) — Y-offsets from each endpoint. + ( + (fx + dx * 0.30, fy + ctrl-val.at(0)), + (tx - dx * 0.30, ty + ctrl-val.at(1)), + ) + } else { + if curved and len > 0 { + let v-reach = calc.abs(dy) + let h-reach = calc.abs(dx) + // ctrl1: departs with upward bias, scaling with whichever reach dominates. + let lift1 = calc.max(v-reach * 0.50, h-reach * 0.20, 0.50) + // ctrl2: arrives from below target — dip scales with vertical distance. + let dip2 = calc.max(v-reach * 0.25, 0.40) + ( + (fx + dx * 0.30, fy + lift1), + (tx - dx * 0.10, ty - dip2), + ) + } else { (none, none) } + } + + // ── Tangent at tip & pullback ─────────────────────────────────── + let (tang-x, tang-y) = if ctrl2 != none { + let ex = tx - ctrl2.at(0) + let ey = ty - ctrl2.at(1) + let ed = calc.sqrt(ex * ex + ey * ey) + if ed > 0 { (ex / ed, ey / ed) } else { (dx / len, dy / len) } + } else { + (dx / len, dy / len) + } + let hb = calc.min(head-back, len * 0.4) + let hax = tx - tang-x * hb + let hay = ty - tang-y * hb + + let shaft-stroke = (paint: paint, thickness: 0.018, dash: "dashed") + let head-stroke = (paint: paint, thickness: 0.018) + let mark-style = (end: ">", fill: paint, scale: 0.5) + + // Dashed shaft + if ctrl1 != none { + bezier((fx, fy), (hax, hay), ctrl1, ctrl2, stroke: shaft-stroke) + } else { + line((fx, fy), (hax, hay), stroke: shaft-stroke) + } + // Solid arrowhead + let tiny = 0.01 + line( + (hax - tang-x * tiny, hay - tang-y * tiny), + (tx, ty), + stroke: head-stroke, + mark: mark-style, + ) + } + } + }) + }) + } // context +} diff --git a/packages/preview/phonokit/0.5.11/grids.typ b/packages/preview/phonokit/0.5.11/grids.typ new file mode 100644 index 0000000000..0009963c28 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/grids.typ @@ -0,0 +1,114 @@ +#import "ipa.typ": ipa-to-unicode +#import "_config.typ": phonokit-font + +// Helper function to parse string-based input like "te2.ne1.see3" +#let parse-grid-string(input) = { + let units = input.split(".") + let parsed = () + + for unit in units { + if unit.len() > 0 { + // Extract the last character as the level + let level-str = unit.at(unit.len() - 1) + let level = int(level-str) + + // Extract everything except the last character as the syllable + let syllable = unit.slice(0, unit.len() - 1) + + parsed.push((text: syllable, level: level)) + } + } + + parsed +} + +// Main metrical grid function - creates metrical grid representations +// Supports two input formats: +// +// 1. String format (simple, not IPA-compatible): +// met-grid("te2.ne1.see3.Ti3.tans1") +// Format: syllable + level number, separated by dots +// +// 2. Array format (IPA-compatible): +// met-grid(("te", 2), ("ne", 1), ("see", 3)) +// met-grid(("te", 2), ("ne", 1), ("see", 3), ipa: true) // Auto-converts strings to IPA +// Format: array of (content, level) tuples +// +#let met-grid(..args, ipa: true) = { + let data = () + + // Determine input format + if args.pos().len() == 1 and type(args.pos().at(0)) == str { + // String format: "te2.ne1.see3" + data = parse-grid-string(args.pos().at(0)) + } else { + // Array format: ("te", 2), ("ne", 1), ... + for arg in args.pos() { + if type(arg) == array and arg.len() == 2 { + let text-content = arg.at(0) + let level = arg.at(1) + + // If ipa mode is enabled and text-content is a string, convert it + if ipa and type(text-content) == str { + text-content = context text(font: phonokit-font.get(), ipa-to-unicode(text-content)) + } + + data.push((text: text-content, level: level)) + } else { + return text(fill: red, weight: "bold")[⚠ Error: Each argument must be a (text, level) tuple] + } + } + } + + if data.len() == 0 { + return text(fill: red, weight: "bold")[⚠ Error: No data to display] + } + + // Find maximum level to determine number of rows + let max-level = 0 + for item in data { + if item.level > max-level { + max-level = item.level + } + } + + // Build the table rows from top to bottom + let rows = () + + // Create rows for each stress level (from highest to lowest) + for level in range(max-level, 0, step: -1) { + let row = () + for item in data { + if item.level >= level { + row.push($times$) + } else { + row.push([]) + } + } + rows.push(row) + } + + // Add the syllable row at the bottom + let syllable-row = () + for item in data { + if type(item.text) == str { + syllable-row.push(context text(font: phonokit-font.get(), item.text)) + } else { + syllable-row.push(item.text) + } + } + rows.push(syllable-row) + + // Create table with no borders, wrapped in box for inline placement + // baseline: 50% centers the grid vertically with text baseline + box( + baseline: 50%, + table( + columns: data.len(), + stroke: none, + align: center, + inset: 8pt, + ..rows.flatten() + ) + ) +} diff --git a/packages/preview/phonokit/0.5.11/hasse.typ b/packages/preview/phonokit/0.5.11/hasse.typ new file mode 100644 index 0000000000..544bc82331 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/hasse.typ @@ -0,0 +1,460 @@ +// Hasse diagram visualization for OT constraint rankings +// Part of phonokit package + +#import "@preview/cetz:0.5.2" +#import "_config.typ": phonokit-font + +/// Create a Hasse diagram for Optimality Theory constraint rankings +/// +/// A Hasse diagram represents the partial order of constraint rankings, +/// showing only minimal domination relationships (transitive reduction). +/// Constraints higher in the diagram dominate those lower. +/// +/// Features: +/// - Supports partial orders (not all constraints need to be ranked) +/// - Handles floating constraints with no ranking relationships +/// - Automatically computes transitive reduction +/// - Auto-scales for complex hierarchies +/// +/// Arguments: +/// - rankings (array): Array of tuples representing rankings: +/// - Three-element tuple `(A, B, level)` means A dominates B, and A is at stratum `level` (REQUIRED) +/// - Four-element tuple `(A, B, level, style)` means A dominates B, A at stratum `level`, with line `style` +/// - Single-element tuple `(A,)` means A is floating (no ranking) +/// - Line styles: "solid" (default), "dashed", "dotted" +/// - Note: Level specification is REQUIRED for all edges to ensure proper stratification +/// - scale (number or auto): Scale factor for diagram (default: auto-scales based on complexity) +/// - node-spacing (number): Horizontal spacing between nodes (default: 2.5) +/// - level-spacing (number): Vertical spacing between levels (default: 1.5) +/// +/// Returns: A Hasse diagram showing the constraint hierarchy +/// +/// Example: +/// ``` +/// #hasse( +/// ( +/// ("Onset", "NoCoda", 0), +/// ("Onset", "Dep", 0), +/// ("Max", "Dep", 0), +/// ("Max", "NoCoda", 0), +/// ("Faith",) // floating constraint +/// ) +/// ) +/// +/// // With line styles +/// #hasse( +/// ( +/// ("Ident[F]", "Agree[place]", 0, "dashed"), // Level 0, dashed line +/// ("Dep", "Agree[vce]", 0), // Level 0, solid line (default) +/// ("Max", "Dep", 1, "dotted"), // Level 1, dotted line +/// ) +/// ) +/// ``` +#let hasse( + rankings, + scale: auto, + node-spacing: 2.5, + level-spacing: 1.5, +) = { + // Validate input + assert(type(rankings) == array, message: "rankings must be an array of tuples") + assert(rankings.len() > 0, message: "rankings array cannot be empty") + + // Extract all constraints and build graph structure + let all-constraints = () + let edges = () // (from, to) pairs + let floating = () + let user-specified-levels = (:) // Track user-specified levels + let edge-styles = (:) // Track line styles for edges + + for ranking in rankings { + assert(type(ranking) == array, message: "Each ranking must be a tuple (array)") + + if ranking.len() == 1 { + // Floating constraint + let constraint = ranking.at(0) + if constraint not in floating { + floating.push(constraint) + } + if constraint not in all-constraints { + all-constraints.push(constraint) + } + } else if ranking.len() >= 3 and ranking.len() <= 4 { + // Domination relationship (level specification is REQUIRED) + let from = ranking.at(0) + let to = ranking.at(1) + let level = ranking.at(2) + let style = "solid" + + // Validate that level is a number + assert(type(level) == int or type(level) == float, message: "Third element must be a level (number). Use (A, B, level) or (A, B, level, style)") + + if ranking.len() == 4 { + // Fourth element is style + style = ranking.at(3) + assert(type(style) == str, message: "Fourth element must be a line style string") + // Validate style + assert(style in ("solid", "dashed", "dotted"), message: "Line style must be 'solid', 'dashed', or 'dotted'") + } + + if (from, to) not in edges { + edges.push((from, to)) + } + if from not in all-constraints { + all-constraints.push(from) + } + if to not in all-constraints { + all-constraints.push(to) + } + + // Store user-specified level + user-specified-levels.insert(from, level) + + // Store edge style + edge-styles.insert(from + "->" + to, style) + } else { + assert(false, message: "Each ranking tuple must have 1 (floating), 3 (with level), or 4 (with level and style) elements") + } + } + + // Compute transitive reduction (remove redundant edges) + // For each edge (A, C), check if there's a path A -> B -> C + let reduced-edges = () + for edge in edges { + let (from, to) = edge + let is-redundant = false + + // Check if there's an intermediate node B such that (from, B) and (B, to) exist + for potential-middle in all-constraints { + if potential-middle != from and potential-middle != to { + let has-first = (from, potential-middle) in edges + let has-second = (potential-middle, to) in edges + if has-first and has-second { + is-redundant = true + break + } + } + } + + if not is-redundant { + reduced-edges.push(edge) + } + } + + // Compute levels using topological sort + // Start with user-specified levels + let constraint-levels = user-specified-levels + + // Level 0 = constraints with no incoming edges (top-ranked) + // Level k = constraints whose dominators are all at level < k + let unassigned = all-constraints.filter(c => c not in constraint-levels.keys()) + let current-level = 0 + + while unassigned.len() > 0 { + let assigned-this-round = () + + for constraint in unassigned { + // Check if all dominators (if any) are already assigned + let dominators = reduced-edges.filter(e => e.at(1) == constraint).map(e => e.at(0)) + + if dominators.len() == 0 { + // No dominators, can be assigned to current level + constraint-levels.insert(constraint, current-level) + assigned-this-round.push(constraint) + } else { + // Check if all dominators are assigned + let all-assigned = dominators.all(d => d in constraint-levels.keys()) + if all-assigned { + // Assign to one level below the maximum dominator level + let max-dom-level = calc.max(..dominators.map(d => constraint-levels.at(d))) + constraint-levels.insert(constraint, max-dom-level + 1) + assigned-this-round.push(constraint) + } + } + } + + // Remove assigned constraints + unassigned = unassigned.filter(c => c not in assigned-this-round) + current-level += 1 + + // Safety check to prevent infinite loop + if current-level > all-constraints.len() { + break + } + } + + // Assign floating constraints to the bottom + if floating.len() > 0 { + let max-level = if constraint-levels.len() > 0 { + calc.max(..constraint-levels.values()) + } else { + -1 + } + for fc in floating { + constraint-levels.insert(fc, max-level + 1) + } + } + + // Group constraints by level + let max-level-num = if constraint-levels.len() > 0 { + calc.max(..constraint-levels.values()) + } else { + 0 + } + + let levels = range(max-level-num + 1).map(i => ()) + for (constraint, level) in constraint-levels { + levels.at(level).push(constraint) + } + + // Minimize edge crossings using barycenter heuristic + // This reorders constraints at each level to reduce visual clutter + for iteration in range(3) { + // Multiple passes improve layout + // Top-down pass: order by average position of parents + for level-idx in range(1, levels.len()) { + let level-constraints = levels.at(level-idx) + + // Calculate barycenter (average parent position) for each constraint + let constraint-barycenters = () + for constraint in level-constraints { + let parents = reduced-edges.filter(e => e.at(1) == constraint).map(e => e.at(0)) + + if parents.len() > 0 { + // Find parent indices in previous level + let parent-indices = parents.map(p => { + let idx = levels.at(level-idx - 1).position(c => c == p) + if idx == none { 0 } else { idx } + }) + let barycenter = parent-indices.sum() / parents.len() + constraint-barycenters.push((constraint, barycenter)) + } else { + // No parents, keep original position + let original-idx = level-constraints.position(c => c == constraint) + constraint-barycenters.push((constraint, original-idx)) + } + } + + // Sort by barycenter + constraint-barycenters = constraint-barycenters.sorted(key: item => item.at(1)) + levels.at(level-idx) = constraint-barycenters.map(item => item.at(0)) + } + + // Bottom-up pass: order by average position of children + for level-idx in range(levels.len() - 1).rev() { + let level-constraints = levels.at(level-idx) + + // Calculate barycenter (average child position) for each constraint + let constraint-barycenters = () + for constraint in level-constraints { + let children = reduced-edges.filter(e => e.at(0) == constraint).map(e => e.at(1)) + + if children.len() > 0 { + // Find child indices in next level + let child-indices = children.map(c => { + let idx = levels.at(level-idx + 1).position(ch => ch == c) + if idx == none { 0 } else { idx } + }) + let barycenter = child-indices.sum() / children.len() + constraint-barycenters.push((constraint, barycenter)) + } else { + // No children, keep original position + let original-idx = level-constraints.position(c => c == constraint) + constraint-barycenters.push((constraint, original-idx)) + } + } + + // Sort by barycenter + constraint-barycenters = constraint-barycenters.sorted(key: item => item.at(1)) + levels.at(level-idx) = constraint-barycenters.map(item => item.at(0)) + } + } + + // Determine scale + let scale-factor = if scale == auto { + if all-constraints.len() > 8 { + 0.7 + } else if all-constraints.len() > 5 { + 0.85 + } else { + 1.0 + } + } else { + scale + } + + // Adjust node spacing based on constraint name lengths to prevent overlap + // Estimate text width: roughly 0.12 units per character in smallcaps at 10pt + let max-constraint-length = calc.max(..all-constraints.map(c => c.len())) + let estimated-width = max-constraint-length * 0.12 * scale-factor + let min-spacing = estimated-width + 0.4 // Add padding + let adjusted-node-spacing = calc.max(node-spacing, min-spacing) + + // Calculate layout positions for ranked constraints + let positions = (:) + for (level-idx, level-constraints) in levels.enumerate() { + let n = level-constraints.len() + let total-width = (n - 1) * adjusted-node-spacing + let start-x = -total-width / 2 + + for (i, constraint) in level-constraints.enumerate() { + let x = start-x + i * adjusted-node-spacing + let y = -level-idx * level-spacing + positions.insert(constraint, (x, y)) + } + } + + // Align one-to-one chains vertically for straight lines + // If a constraint has exactly one parent and that parent has exactly one child, + // align them vertically + for (constraint, pos) in positions { + let parents = reduced-edges.filter(e => e.at(1) == constraint).map(e => e.at(0)) + + if parents.len() == 1 { + let parent = parents.at(0) + let parent-children = reduced-edges.filter(e => e.at(0) == parent).map(e => e.at(1)) + + if parent-children.len() == 1 and parent in positions { + // One-to-one relationship: align x-coordinates + let parent-pos = positions.at(parent) + let (parent-x, parent-y) = parent-pos + let (child-x, child-y) = pos + + // Update child's x to match parent's x + positions.insert(constraint, (parent-x, child-y)) + } + } + // Note: For constraints with multiple parents, the barycenter heuristic + // already positioned them optimally to minimize crossings. Don't override. + } + + // Re-center the diagram for symmetry + // Calculate the average x position and shift to center at 0 + if positions.len() > 0 { + let all-x = positions.values().map(pos => pos.at(0)) + let min-x = calc.min(..all-x) + let max-x = calc.max(..all-x) + let center-offset = -(min-x + max-x) / 2 + + // Apply centering offset + let centered-positions = (:) + for (constraint, pos) in positions { + let (x, y) = pos + centered-positions.insert(constraint, (x + center-offset, y)) + } + positions = centered-positions + } + + // Draw the diagram + box(inset: 1.2em, baseline: 40%, cetz.canvas(length: scale-factor * 1cm, { + import cetz.draw: * + + // Draw edges first (so they appear behind nodes) + for edge in reduced-edges { + let (from, to) = edge + if from in positions and to in positions { + let (x1, y1) = positions.at(from) + let (x2, y2) = positions.at(to) + + // Get line style for this edge + let edge-key = from + "->" + to + let line-style = if edge-key in edge-styles { + edge-styles.at(edge-key) + } else { + "solid" + } + + // Scale stroke width with scale factor + let stroke-width = 0.8pt * scale-factor + + // Create stroke based on style + let stroke-style = if line-style == "dashed" { + (paint: black, thickness: stroke-width, dash: "dashed") + } else if line-style == "dotted" { + (paint: black, thickness: stroke-width, dash: "dotted") + } else { + stroke-width + black + } + + // Draw line + line((x1, y1), (x2, y2), stroke: stroke-style) + + // Draw arrow head (also scaled) + let arrow-size = 0.15 * scale-factor + let dx = x2 - x1 + let dy = y2 - y1 + let length = calc.sqrt(dx * dx + dy * dy) + let ux = dx / length + let uy = dy / length + + // Arrow at destination (always solid) + let arrow-x = x2 - uy * arrow-size + let arrow-y = y2 + ux * arrow-size + line((x2, y2), (arrow-x, arrow-y), stroke: stroke-width + black) + + arrow-x = x2 + uy * arrow-size + arrow-y = y2 - ux * arrow-size + line((x2, y2), (arrow-x, arrow-y), stroke: stroke-width + black) + } + } + + // Draw white rectangles behind constraint names (to create whitespace) + for (constraint, pos) in positions { + let (x, y) = pos + // Estimate text dimensions based on constraint name length + let name-length = constraint.len() + // Width: roughly 0.22 units per character (generous to ensure full coverage) + let text-width = name-length * 0.22 * scale-factor + // Height: based on font size (increased to cover actual rendered height) + let text-height = 0.5 * scale-factor + // Add padding + let padding = 0.50 + let rect-width = text-width + padding + let rect-height = text-height + padding + + // Draw rounded rectangle centered at constraint position + rect( + (x - rect-width / 2, y - rect-height / 2), + (x + rect-width / 2, y + rect-height / 2), + fill: white, + stroke: none, + radius: 0.6, + ) + } + + // Draw constraint names in smallcaps (but not text inside brackets) + for (constraint, pos) in positions { + let (x, y) = pos + + // Parse constraint name to apply smallcaps only outside brackets + let formatted-name = { + // Match pattern: text before bracket, bracket content, text after + let bracket-match = constraint.match(regex("^([^\[]*)\[([^\]]*)\](.*)$")) + + if bracket-match != none { + // Has brackets: smallcaps before, regular inside brackets, smallcaps after + let before = bracket-match.captures.at(0) + let inside = bracket-match.captures.at(1) + let after = bracket-match.captures.at(2) + + // Compose the parts using content block + [#smallcaps(before)\[#inside\]#if after != "" { smallcaps(after) }] + } else { + // No brackets: all smallcaps + smallcaps(constraint) + } + } + + content( + (x, y), + padding: 0.1, + anchor: "center", + context text( + font: phonokit-font.get(), + size: 10pt * scale-factor, + formatted-name, + ), + ) + } + })) +} diff --git a/packages/preview/phonokit/0.5.11/intonational.typ b/packages/preview/phonokit/0.5.11/intonational.typ new file mode 100644 index 0000000000..4d4dd2552f --- /dev/null +++ b/packages/preview/phonokit/0.5.11/intonational.typ @@ -0,0 +1,80 @@ +#import "_config.typ": phonokit-font + +/// Place a ToBI label above the current inline text position +/// +/// Designed to be placed inline immediately after the syllable or word being annotated. +/// The label floats above the text at the insertion point, optionally connected by +/// a vertical stem. +/// +/// Always pass labels as *strings* (e.g. `"H*"`), not bare content (`[H*]`), since +/// characters like `*` and `_` have special meaning in Typst markup. Use ASCII +/// hyphens for phrase accents (e.g. `"L-"`, `"L-H%"`); en/em dashes are +/// automatically normalized to hyphens. Double hyphens are converted to superscript "-". +/// +/// Arguments: +/// - label (string): ToBI label +/// - line (boolean): draw a vertical stem connecting label to text (default: true) +/// - height (length): distance from text baseline to the bottom of the label (default: 1.8em) +/// - lift (length): gap between text baseline and stem bottom (default: 0.6em) +/// - gap (length): gap between stem top and label bottom (default: 0.15em) +/// - en-dash (boolean): render phrase-accent hyphens as en dashes (default: false) +/// +/// Example: +/// ``` +/// You're a were#int("*L")wolf?#h(1em)#int("H%", line: false) + +/// ``` +#let int( + label, + line: true, + height: 2em, + lift: 0.8em, + gap: 0.22em, + en-dash: true, +) = context { + // Normalize all dash variants to ASCII hyphen, then output in the desired + // form. U+2011 (non-breaking hyphen, class GL) never breaks. For en-dash + // rendering, U+2060 (word joiner, class WJ) prepended to U+2013 suppresses + // any break opportunity around the en dash. + let single-dash = s => if en-dash { s.replace("-", "\u{2060}\u{2013}\u{2060}") } else { s.replace("-", "\u{2011}") } + let normalize-str = s => single-dash( + s.replace("\u{2011}", "-").replace("\u{2013}", "-").replace("\u{2014}", "-"), + ) + // "--" becomes a superscript en-dash (Zsiga-style phrase accent). + // Normalize other dashes first, then split on "--" before the + // single-dash replacement so the two hyphens aren't individually + // converted. Use a for loop to build content instead of .join() + // to avoid content-separator ambiguity. + let lbl-text = if type(label) == str { + let base = label.replace("\u{2011}", "-").replace("\u{2013}", "-").replace("\u{2014}", "-") + if base.contains("--") { + let parts = base.split("--").map(single-dash) + text(font: phonokit-font.get(), size: 0.8em, { + parts.at(0) + for i in range(1, parts.len()) { + super(size: 0.90em, [–]) + parts.at(i) + } + }) + } else { + text(font: phonokit-font.get(), size: 0.8em, normalize-str(label)) + } + } else { + text(font: phonokit-font.get(), size: 0.8em, label) + } + let lbl-w = measure(lbl-text).width + let lbl-h = measure(lbl-text).height + let lbl = box(width: lbl-w, lbl-text) + // baseline: 0pt places the box bottom at the text baseline, + // so the box extends upward to reserve space for the annotation. + // place() positions elements absolutely so surrounding alignment cannot shift them. + let stem-h = height - lift - gap + box(width: 0pt, height: height + lbl-h, baseline: 0pt, { + // Stem (anchored at bottom-left, offset upward by lift) + if line { + place(bottom + left, dy: -lift, rect(width: 0.05em, height: stem-h, fill: black, stroke: none)) + } + // Label (anchored at top, centered horizontally via dx) + place(top + left, dx: -lbl-w / 2, lbl) + }) +} diff --git a/packages/preview/phonokit/0.5.11/ipa.typ b/packages/preview/phonokit/0.5.11/ipa.typ new file mode 100644 index 0000000000..e3fbc65f3c --- /dev/null +++ b/packages/preview/phonokit/0.5.11/ipa.typ @@ -0,0 +1,412 @@ +// Convert tipa-style notation to IPA Unicode (without font styling) +// This is exported separately so other modules can use the conversion logic +#let ipa-to-unicode(input) = { + // Define TIPA to IPA mappings + let mappings = ( + // CONSONANTS - Plosives + "p": "p", + "b": "b", + "t": "t", + "d": "d", + "\\:t": "ʈ", + "\\:d": "ɖ", + "\\textbardotlessj": "ɟ", + "\\barredj": "ɟ", + "c": "c", + "k": "k", + "g": "ɡ", + "q": "q", + "\\;G": "ɢ", + "?": "ʔ", + "P": "ʔ", + // CONSONANTS - Nasals + "m": "m", + "M": "ɱ", + "n": "n", + "\\:n": "ɳ", + "\\textltailn": "ɲ", + "N": "ŋ", + "\\;N": "ɴ", + "\\nh": "ɲ", + // CONSONANTS - Trills + "\\;B": "ʙ", + "r": "r", + "\\;R": "ʀ", + // CONSONANTS - Tap or Flap + "R": "ɾ", + "\\:r": "ɽ", + // CONSONANTS - Fricatives + "f": "f", + "v": "v", + "F": "ɸ", + "B": "β", + "T": "θ", + "D": "ð", + "s": "s", + "z": "z", + "S": "ʃ", + "Z": "ʒ", + "\\:s": "ʂ", + "\\:z": "ʐ", + "\\c{c}": "ç", + "C": "ç", + "J": "ʝ", + "x": "x", + "G": "ɣ", + "X": "χ", + "K": "ʁ", + "\\textcrh": "ħ", + "\\barredh": "ħ", + "Q": "ʕ", + "h": "h", + "H": "ɦ", + // CONSONANTS - Lateral Fricatives + "\\textbeltl": "ɬ", + "\\textlyoghlig": "ɮ", + "\\l3": "ɮ", + // CONSONANTS - Approximants + "V": "ʋ", + "\\*r": "ɹ", + "j": "j", + "\\textturnmrleg": "ɰ", + "\\mw": "ɰ", + "\\:R": "ɻ", + // CONSONANTS - Lateral Approximants + "l": "l", + "\\:l": "ɭ", + "L": "ʎ", + "\\;L": "ʟ", + // CONSONANTS - Velarized l + "\\darkl": "ɫ", + // OTHER CONSONANTS - Clicks + "\\!o": "ʘ", + "\\textdoublebarpipe": "ǂ", + "\\doublebarpipe": "ǂ", + "||": "ǁ", + // OTHER CONSONANTS - Other + "\\textbarglotstop": "ʡ", + "\\barredP": "ʡ", + // OTHER CONSONANTS - Implosives + "\\!b": "ɓ", + "\\!d": "ɗ", + "\\!j": "ʄ", + "\\!g": "ɠ", + "\\!G": "ʛ", + // OTHER CONSONANTS - Additional Fricatives + "\\*w": "ʍ", + "\\texththeng": "ɧ", + "\\;H": "ʜ", + "\\textctz": "ʑ", + "\\textbarrevglotstop": "ʢ", + "\\barrevglotstop": "ʢ", + // OTHER CONSONANTS - Approximant/Flap + "\\textturnlonglegr": "ɺ", + "\\turnlonglegr": "ɺ", + // VOWELS - Close + "i": "i", + "I": "ɪ", + "y": "y", + "Y": "ʏ", + "1": "ɨ", + "0": "ʉ", + "W": "ɯ", + "u": "u", + "U": "ʊ", + // VOWELS - Close-mid/Mid + "e": "e", + "\\o": "ø", + "9": "ɘ", + "8": "ɵ", + "7": "ɤ", + "o": "o", + // VOWELS - Mid + "@": "ə", + // VOWELS - Open-mid + "E": "ɛ", + "\\oe": "œ", + "3": "ɜ", + "\\textcloseepsilon": "ɞ", + "\\closeepsilon": "ɞ", + "2": "ʌ", + "O": "ɔ", + // VOWELS - Near-open/Open + "\\ae": "æ", + "\\OE": "ɶ", + "a": "a", + "5": "ɐ", + "A": "ɑ", + "6": "ɒ", + "\\schwar": "ɚ", + "\\epsilonr": "ɝ", + // SUPRASEGMENTALS + "'": "ˈ", // primary stress + ",": "ˌ", // secondary stress + ":": "ː", // length mark + // SPACING + "\\s": " ", // space + // ARCHIPHONEMES escaped + "\\A": "A", + "\\B": "B", + "\\C": "C", + "\\D": "D", + "\\E": "E", + "\\F": "F", + "\\G": "G", + "\\H": "H", + "\\I": "I", + "\\J": "J", + "\\K": "K", + "\\L": "L", + "\\M": "M", + "\\N": "N", + "\\O": "O", + "\\P": "P", + "\\Q": "Q", + "\\R": "R", + "\\S": "S", + "\\T": "T", + "\\U": "U", + "\\V": "V", + "\\W": "W", + "\\X": "X", + "\\Y": "Y", + "\\Z": "Z", + // TIPA LONG-FORM ALTERNATIVES AND ADDITIONAL SYMBOLS + // A + "\\textturna": "ɐ", + "\\textscripta": "ɑ", + "\\textturnscripta": "ɒ", + "\\textsca": "ᴀ", + "\\;A": "ᴀ", + "\\textturnv": "ʌ", + // B + "\\texthtb": "ɓ", + "\\textscb": "ʙ", + "\\textcrb": "ƀ", + "\\textbarb": "ƀ", + "\\textbeta": "β", + "\\textsoftsign": "ь", + "\\texthardsign": "ъ", + // C + "\\textbarc": "ȼ", + "\\texthtc": "ƈ", + "\\v{c}": "č", + "\\textctc": "ɕ", + "\\textstretchc": "ʗ", + // D + "\\textcrd": "đ", + "\\textbard": "đ", + "\\texthtd": "ɗ", + "\\textrtaild": "ɖ", + "\\textctd": "ȡ", + "\\textdzlig": "ʣ", + "\\textdctzlig": "ʥ", + "\\textdyoghlig": "ʤ", + "\\dh": "ð", + // E + "\\textschwa": "ə", + "\\textrhookschwa": "ɚ", + "\\textreve": "ɘ", + "\\textsce": "ᴇ", + "\\;E": "ᴇ", + "\\textepsilon": "ɛ", + "\\textrevepsilon": "ɜ", + "\\textrhookrevepsilon": "ɝ", + "\\textcloserevepsilon": "ɞ", + // G + "\\textg": "ɡ", + "\\textbarg": "ǥ", + "\\textcrg": "ǥ", + "\\texthtg": "ɠ", + "\\textscg": "ɢ", + "\\texthtscg": "ʛ", + "\\textgamma": "ɣ", + "\\textbabygamma": "ɤ", + "\\textramshorns": "ɤ", + // H + "\\texthvlig": "ƕ", + "\\texthth": "ɦ", + "\\textturnh": "ɥ", + "4": "ɥ", + "\\textsch": "ʜ", + // I + "\\i": "ı", + "\\textbari": "ɨ", + "\\textiota": "ɩ", + "\\textlhti": "ɩ", + "\\textsci": "ɪ", + // J + "\\j": "ȷ", + "\\textctj": "ʝ", + "\\textscj": "ᴊ", + "\\;J": "ᴊ", + "\\v{\\j}": "ǰ", + "\\textObardotlessj": "ɟ", + "\\texthtbardotlessj": "ʄ", + // K + "\\texthtk": "ƙ", + "\\textturnk": "ʞ", + // L + "\\textltilde": "ɫ", + "\\textbarl": "ł", + "\\textrtaill": "ɭ", + "\\textOlyoghlig": "ɮ", + "\\textscl": "ʟ", + "\\textlambda": "λ", + "\\textcrlambda": "ƛ", + // M + "\\textltailm": "ɱ", + "\\textturnm": "ɯ", + // N + "\\textnrleg": "ƞ", + "\\ng": "ŋ", + "\\textrtailn": "ɳ", + "\\textctn": "ȵ", + "\\textscn": "ɴ", + // O + "\\textbullseye": "ʘ", + "\\textbaro": "ɵ", + "\\textscoelig": "ɶ", + "\\textopeno": "ɔ", + "\\textomega": "ω", + "\\textcloseomega": "ɷ", + // P + "\\textwynn": "ƿ", + "\\textthorn": "þ", + "\\th": "þ", + "\\texthtp": "ƥ", + "\\textphi": "ɸ", + // Q + "\\texthtq": "ʠ", + // R + "\\textfishhookr": "ɾ", + "\\textlonglegr": "ɼ", + "\\textrtailr": "ɽ", + "\\textturnr": "ɹ", + "\\textturnrrtail": "ɻ", + "\\textscr": "ʀ", + "\\textinvscr": "ʁ", + // S + "\\v{s}": "š", + "\\textrtails": "ʂ", + "\\textesh": "ʃ", + "\\textctesh": "ʆ", + // T + "\\texthtt": "ƭ", + "\\textlhookt": "ƫ", + "\\textrtailt": "ʈ", + "\\texttctclig": "ʨ", + "\\texttslig": "ʦ", + "\\texteshlig": "ʧ", + "\\textturnt": "ʇ", + "\\textctt": "ȶ", + "\\texttheta": "θ", + // U + "\\textbaru": "ʉ", + "\\textupsilon": "ʊ", + "\\textscu": "ᴜ", + "\\;U": "ᴜ", + // V + "\\textscriptv": "ʋ", + // W + "\\textturnw": "ʍ", + // X + "\\textchi": "χ", + // Y + "\\textturny": "ʎ", + "\\textscy": "ʏ", + // Z + "\\textcommatailz": "ʐ", + "\\v{z}": "ž", + "\\textrevyogh": "ʕ", + "\\textrtailz": "ʐ", + "\\textyogh": "ʒ", + "\\textctyogh": "ʓ", + "\\textcrtwo": "ƻ", + "\\textglotstop": "ʔ", + "\\textraiseglotstop": "ˀ", + "\\textinvglotstop": "ʖ", + "\\textrevglotstop": "ʕ", + ) + + // Define combining diacritics + // Forward-looking: precede the phoneme in input (e.g., \~ a → ã) + let forward_diacritics = ( + "\\~": "̃", // combining tilde (nasalization) + "\\r": "̥", // combining ring below (devoicing) + "\\v": "̩", // combining vertical line below (voicing) + "\\t": "͡", // combining double inverted breve (tie bar for affricates) + "\\dental": "̪", // no trailing space + ) + + // Backward-looking: follow the phoneme in input (e.g., p \h → pʰ) + let backward_diacritics = ( + "\\*": "̚", // combining left angle above (unreleased) + "\\h": "ʰ", // modifier letter small h (aspirated) + "\\velar": "ˠ", + "\\palatal": "ʲ", + "\\labial": "ʷ", + "\\ej": "ʼ", // modifier letter apostrophe (ejective) + ) + + // Split by spaces and process each token + let tokens = input.split(" ") + let result = "" + let i = 0 + let pending_diacritic = none + + while i < tokens.len() { + let token = tokens.at(i) + + // Check if this token is a forward-looking diacritic + if token in forward_diacritics { + // Store it to apply to next character + pending_diacritic = forward_diacritics.at(token) + } else if token in backward_diacritics { + // Apply immediately to previous character + result += backward_diacritics.at(token) + } else if token.contains("\\") { + // Backslash command + if token in mappings { + result += mappings.at(token) + // Apply pending diacritic if any + if pending_diacritic != none { + result += pending_diacritic + pending_diacritic = none + } + } else { + result += token + } + } else { + // No backslash: split into individual characters + let chars = token.clusters() + for (idx, char) in chars.enumerate() { + if char in mappings { + result += mappings.at(char) + } else { + result += char + } + // Apply pending diacritic to first character only + if idx == 0 and pending_diacritic != none { + result += pending_diacritic + pending_diacritic = none + } + } + } + + i += 1 + } + + result +} + +// Main IPA function: converts tipa-style notation to IPA +#import "_config.typ": phonokit-font + +#let ipa(input) = { + let rendered = ipa-to-unicode(input) + context { + metadata(rendered) + text(font: phonokit-font.get(), rendered) + } +} diff --git a/packages/preview/phonokit/0.5.11/lib.typ b/packages/preview/phonokit/0.5.11/lib.typ new file mode 100644 index 0000000000..98e61e0398 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/lib.typ @@ -0,0 +1,1148 @@ +// phonokit - a toolkit to create phonological representations +// Author: Guilherme D. Garcia +// +// This package provides: +// - IPA transcription with tipa-style input syntax +// - Prosodic structure visualization (syllables, moras, feet, prosodic words) +// - Autosegmental representations and processes (features and tones) +// - IPA vowel charts (trapezoid) with language inventories +// - IPA consonant tables (pulmonic) with language inventories +// - Optimality Theory (OT) tableaux with violation marking and shading +// - Harmonic Grammar (HG) tableaux with weighted harmony calculations +// - Noisy Harmonic Grammar (NHG) tableaux with stochastic simulations +// - Maximum Entropy (MaxEnt) grammar tableaux with probability calculations +// - SPE-style feature matrices for phonological representations +// - Feature-geometry trees (Clements & Hume 1995; Sagey 1986) + +// Import modules +#import "_config.typ": phonokit-init +#import "ipa.typ": * +#import "prosody.typ": * +#import "ot.typ": * +#import "hasse.typ": * +#import "extras.typ": * +#import "vowels.typ": * +#import "grids.typ": * +#import "consonants.typ": * +#import "features.typ": * +#import "sonority.typ": * +#import "autosegmental.typ": * +#import "multi-tier.typ": * +#import "sound-shift.typ": * +#import "ex.typ": ex, ex-rules +#import "intonational.typ": * +#import "geom.typ": * +#import "phonetics.typ": * + +/// Initialize phonokit settings +/// +/// Call this at the top of your document to configure package-wide settings. +/// Currently supports setting a custom font for all phonokit functions. +/// +/// Arguments: +/// - font (string): Font name to use for IPA rendering (default: "Charis") +/// +/// Example: +/// ``` +/// #import "@preview/phonokit:0.5.11": * +/// #phonokit-init(font: "Libertinus Serif") +/// ``` +#let phonokit-init = phonokit-init + +/// Convert tipa-style notation to IPA symbols +/// +/// Supports: +/// - IPA consonants and vowels +/// - Combining diacritics (\\~, \\r, \\v, \\t for nasal, devoiced, voiced, tie) +/// - Suprasegmentals (' and , for primary and secondary stress; : for length) +/// - Automatic character splitting for efficiency +/// +/// Example: `#ipa("'hEloU")` produces ˈhɛloʊ +/// +/// Arguments: +/// - input (string): tipa-style notation +/// +/// Returns: IPA symbols in the configured font (default: Charis) +#let ipa = ipa + +/// Visualize sonority profiles based on Parker (2011) +/// +/// Generates a visual sonority profile for a given phonemic transcription. +/// Phonemes are mapped to a vertical sonority axis (1-13) based on Parker's +/// acoustic scale and connected to show the sonority contour of the word. +/// +/// Features: +/// - Uses Parker (2011) hierarchy (e.g., Flaps > Laterals > Trills) +/// - Visualizes syllable boundaries using alternating background shading (white/gray) +/// - Automatic parsing of tipa-style input strings +/// +/// Arguments: +/// - word (string): Phonemic string in tipa-style (use "." for syllable boundaries) +/// - syl (none): Legacy parameter, kept for API compatibility +/// - stressed (int, optional): Index of stressed syllable (default: none) +/// - box-size (float): Size of individual phoneme boxes (default: 0.8) +/// - scale (float): Overall scale factor for the diagram (default: 1.0) +/// - y-range (array): Vertical axis range for plotting (default: (0, 8)) +/// - show-lines (bool): Connect phonemes with dashed lines (default: true) +/// +/// Returns: CeTZ drawing of the sonority profile +/// +/// Example: +/// ``` +/// // Visualizes "par.to.me" with 3 distinct background zones +/// #sonority("par.to.me") +/// +/// // Demonstrates Flap (R) > Lateral (l) ranking +/// #sonority("ka.Ra.lo") +/// ``` +/// +/// Note: Input is automatically truncated to the first 10 phonemes to prevent +/// visual overflow. +#let sonority = sonority + +/// Create an illustrative F1/F2 vowel cloud for teaching. +/// +/// Generates synthetic vowel tokens around built-in F1/F2 means and displays +/// them on an inverted F1/F2 diagram. +/// +/// Arguments: +/// - vowels (string): Tipa-style/IPA vowel string or a built-in language name +/// such as `"english"` +/// - source (array, optional): Tabular data from `csv(...)` with required +/// columns `vowel`, `f1`, and `f2`; a plain `csv("...")` table with a +/// header row is accepted +/// - sd (float): Spread of synthetic tokens in Hz +/// - sd2 (float, optional): Optional separate F2 spread in Hz +/// - n (int): Number of tokens per vowel (default: 10) +/// - seed (int): Deterministic seed for token placement (default: 1) +/// - labels (bool): Show vowel labels at the means (default: true) +/// - points (bool): Show vowel tokens (default: true) +/// - centers (bool): Show explicit `+` mean markers (default: false) +/// - ellipse (bool): Show 1-SD ellipses centered on the means (default: false) +/// - ellipse-stroke (stroke or auto): Stroke for SD ellipses +/// (default: `0.8pt + luma(190)`) +/// - ellipse-fill (fill): Fill for SD ellipses (default: none) +/// - grid (bool): Show the background grid (default: true) +/// - color-by-vowel (bool): Use a color cycle by vowel category (default: true) +/// - point-size (int): Marker size for synthetic tokens (default: 50) +/// - point-color (color or auto): Override token color (default: auto) +/// - point-alpha (ratio): Token transparency (default: 20%) +/// - vowel-color (color): Color of vowel labels (default: black) +/// - vowel-size (length): Font size of vowel labels (default: 20pt) +/// - vowel-weight (str): Font weight of vowel labels (default: `"regular"`) +/// - axis-size (length): Font size of axis labels and tick labels (default: 10pt) +/// - scale (float): Overall scale factor for the figure (default: 1.0) +/// - x-label (content): X-axis label (default: `[F2 (Hz)]`) +/// - y-label (content): Y-axis label (default: `[F1 (Hz)]`) +/// - width (length): Diagram width (default: 10cm) +/// - height (length): Diagram height (default: 7cm) +/// +/// Notes: +/// - In synthetic mode, ellipses visualize the user-provided spread parameters +/// (`sd`, `sd2`). +/// - In CSV mode, vowel means and ellipse sizes are computed from the observed +/// tokens in the input data. +/// +/// Example: +/// ``` +/// #formants("italian", scale: 0.6, ellipse: true, axis-size: 1.3em) +/// #formants(source: csv("extras/formants_sample.csv"), scale: 0.8) +/// ``` +#let formants = formants + +/// Draw a schematic voice onset time (VOT) timeline. +/// +/// Positive values show an aspiration interval between release and voicing +/// onset, zero values align release and voicing onset, and negative values +/// show prevoicing. +/// +/// Arguments: +/// - vot (number): Voice onset time in milliseconds +/// - closure (number): Closure duration in milliseconds (default: 40) +/// - vowel (number): Vowel region duration in milliseconds (default: 60) +/// - scale (float): Overall scale factor for the diagram (default: 1.0) +/// - label (auto or bool): Show the VOT value label (default: auto, equivalent +/// to true) +/// - keys (bool): Show event keys and legend (`R`, `V`, and compact interval +/// keys such as `A`; default: false) +/// - ui-lang (string): UI label language. Supported aliases: en/english, +/// fr/french, pt/portuguese (default: "en") +/// - closure-label, release-label, voicing-label, vowel-label, vot-label, +/// interval-label (auto, content, string, or none): Override individual +/// labels. `auto` uses localized defaults; `interval-label: auto` is the +/// localized aspiration label. Set `interval-label: none` to hide it +/// - interval-key (auto or content): Key used when `interval-label` does not +/// fit and `keys: true` (default: auto, usually `A` for aspiration) +/// - closure-segment, interval-segment, vowel-segment (string, content, or none): +/// Optional IPA/segment labels placed below the closure, interval, and +/// vowel regions. Strings use phonokit's IPA parser (default: none) +/// - segment-size (length): Font size for segment labels (default: 10pt) +/// - fill-closure (color): Closure region fill (default: luma(230)) +/// - fill-vowel (color): Vowel region fill (default: white) +/// - fill-aspiration (color): Positive-VOT interval fill (default: luma(245)) +/// - voicing (bool): Draw a schematic voicing/noise waveform (default: true) +/// - voicing-stroke (stroke or auto): Stroke for the voicing waveform (default: +/// auto) +/// +/// Region labels are placed above the boxes and are shown only when their +/// localized or overridden label fits the available region width. Positive-VOT +/// interval labels use the same fit logic; when they do not fit and `keys: +/// true`, the interval key is shown in the diagram and explained in the legend. +/// +/// Example: +/// ``` +/// #vot(65) +/// #vot( +/// 65, +/// closure-segment: "t", +/// interval-segment: "\\h", +/// vowel-segment: "\\ae", +/// ) +/// #vot(-60, voicing: false) +/// #vot(-60, ui-lang: "fr") +/// ``` +#let vot = vot + +/// Draw a single syllable's internal structure +/// +/// Visualizes only the syllable (σ) level with onset, rhyme, nucleus, and coda. +/// +/// Arguments: +/// - input (string): A single syllable (e.g., "ka" or "'va") +/// - scale (float): Scale factor for the diagram (default: 1.0) +/// - symbol (array): Domain labels top-down: (σ) (default: ("σ",)) +/// - distance (float, optional): Horizontal distance between segments (default: none) +/// +/// Returns: CeTZ drawing of syllable structure +/// +/// Example: `#syllable("\\t tS \\ae t", scale: 0.9)` +#let syllable = syllable + +/// Draw a mora-based structure +/// +/// Visualizes mora (μ) and syllable (σ) levels, showing how syllables +/// are decomposed into moras based on weight. +/// +/// Arguments: +/// - input (string): A single syllable (e.g., "kan" or "ka") +/// - coda (bool): Whether codas contribute to weight (default: false) +/// - scale (float): Scale factor for the diagram (default: 1.0) +/// - symbol (array): Domain labels top-down: (σ, μ) (default: ("σ", "μ")) +/// - distance (float, optional): Horizontal distance between segments (default: none) +/// +/// Returns: CeTZ drawing of moraic structure +/// +/// Examples: +/// - `#mora("\\t tS \\ae t", coda: true)` - Moraic representation with coda weight +/// - `#mora("tR \\~ a:m", coda: true)` - Long vowel represented with two moras +#let mora = mora + +/// Draw a foot with syllables +/// +/// Visualizes foot (Σ) and syllable (σ) levels. All syllables are part of the foot. +/// Stressed syllables are marked with an apostrophe before the syllable. +/// +/// Arguments: +/// - input (string): Syllables separated by dots (e.g., "ka.'va.lo") +/// - scale (float): Scale factor for the diagram (default: 1.0) +/// - symbol (array): Domain labels top-down: (Σ, σ) (default: ("Σ", "σ")) +/// - distance (float, optional): Horizontal distance between segments (default: none) +/// +/// Returns: CeTZ drawing of foot structure +/// +/// Example: `#foot("'p \\h \\ae.\\*r Is", scale: 0.9)` +#let foot = foot + +/// Draw a foot with moraic structure +/// +/// Visualizes foot (Σ), syllable (σ), and mora (μ) levels. Combines foot +/// structure with moraic weight representation. +/// Stressed syllables are marked with an apostrophe before the syllable. +/// +/// Arguments: +/// - input (string): Syllables separated by dots (e.g., "po.'Ral") +/// - coda (bool): Whether codas contribute to weight (default: false) +/// - scale (float): Scale factor for the diagram (default: 1.0) +/// - symbol (array): Domain labels top-down: (Σ, σ, μ) (default: ("Σ", "σ", "μ")) +/// - distance (float, optional): Horizontal distance between segments (default: none) +/// +/// Returns: CeTZ drawing of moraic foot structure +/// +/// Examples: +/// - `#foot-mora("po.'Ral", coda: true, scale: 0.9)` - Disyllabic foot with moraic structure +/// - `#foot-mora("'po.Ra.ma", coda: true, scale: 0.9)` - Dactyl with moraic structure +#let foot-mora = foot-mora + +/// Draw a prosodic word structure with explicit foot boundaries +/// +/// Visualizes prosodic word (PWd), foot (Σ), and syllable (σ) levels. +/// Use parentheses to mark foot boundaries. +/// Stressed syllables are marked with an apostrophe before the syllable. +/// +/// Arguments: +/// - input (string): Syllables with optional foot markers in parentheses +/// - foot (string): "R" (right-aligned) or "L" (left-aligned) for PWd alignment (default: "R") +/// - scale (float): Scale factor for the diagram (default: 1.0) +/// - symbol (array): Domain labels top-down: (ω, Σ, σ) (default: ("ω", "Σ", "σ")) +/// - distance (float, optional): Horizontal distance between segments (default: none) +/// +/// Returns: CeTZ drawing of prosodic structure +/// +/// Examples: +/// - `#word("('po.Ra).ma", scale: 0.9)` - One foot plus one unfooted syllable +/// - `#word("('po.Ra).('ma.pa)", foot: "R", scale: 0.9)` - Two feet, right-headed PWd +#let word = word + +/// Draw a prosodic word structure with moraic representation +/// +/// Visualizes prosodic word (PWd), foot (Σ), syllable (σ), and mora (μ) levels. +/// Combines prosodic word structure with moraic weight representation. +/// Use parentheses to mark foot boundaries. +/// Stressed syllables are marked with an apostrophe before the syllable. +/// +/// Arguments: +/// - input (string): Syllables with optional foot markers in parentheses +/// - foot (string): "R" (right-aligned) or "L" (left-aligned) for PWd alignment (default: "R") +/// - coda (bool): Whether codas contribute to weight (default: false) +/// - scale (float): Scale factor for the diagram (default: 1.0) +/// - symbol (array): Domain labels top-down: (ω, Σ, σ, μ) (default: ("ω", "Σ", "σ", "μ")) +/// - distance (float, optional): Horizontal distance between segments (default: none) +/// +/// Returns: CeTZ drawing of moraic prosodic structure +/// +/// Examples: +/// - `#word-mora("('po.Ra).ma", coda: true, scale: 0.9)` - Trochee with unfooted syllable +/// - `#word-mora("('po.Ra).('ma.pa)", foot: "L", coda: true, scale: 0.9)` - Two feet, left-headed PWd +#let word-mora = word-mora + +/// Create a metrical grid representation for stress and rhythm analysis +/// +/// Visualizes hierarchical stress levels using stacked × marks above syllables. +/// This follows metrical grid theory where each level represents a different +/// metrical prominence tier. +/// +/// Supports two input formats: +/// 1. String format (simple, but not IPA-compatible): +/// - Syllables separated by dots, each ending with a stress level number +/// 2. Array format (IPA-compatible): +/// - Array of (syllable, level) tuples +/// +/// Arguments: +/// - ..args: Either a single string or multiple (syllable, level) tuples +/// - String format: syllables separated by dots, each ending with stress level (e.g., "te2.ne1.see3") +/// - Tuple format: pairs of (syllable, level) passed as separate arguments +/// - ipa (bool): Automatically convert strings to IPA notation (default: true) +/// +/// Returns: Table showing syllables with stacked × marks indicating stress levels +/// +/// Examples: +/// - `#met-grid("bu3.tter1.fly2")` - String format +/// - `#met-grid( +/// ("b2", 3), +/// ("R \\schwar", 1), +/// ("flaI", 2), +/// )` - Array format +/// +/// Note: The string format uses numbers to indicate stress levels, which conflicts +/// with IPA numeric symbols. For IPA compatibility, use the array format. +#let met-grid = met-grid + + +/// Plot vowels on an IPA vowel trapezoid +/// +/// Visualizes vowels on the IPA vowel chart (trapezoid) with proper positioning +/// based on frontness, height, and roundedness. Supports language-specific +/// inventories, custom vowel sets, or tipa-style IPA notation. +/// +/// Arguments: +/// - vowel-string (string, optional): Vowel symbols to plot, a built-in language +/// name, or tipa-style IPA, passed positionally (e.g. `vowels("aeiou")` or +/// `vowels("spanish")`). Optional: you may omit it and pass `lang` instead +/// (e.g. `vowels(lang: "spanish")`). If both are given and the positional is a +/// language name, the positional wins. +/// - lang (string, optional): Built-in language whose inventory to plot, used when +/// no language-name positional is given (e.g. `vowels(lang: "spanish")`). Also +/// selects the nasal-vowel preset when the positional is a custom symbol string. +/// - width (float): Base width of trapezoid (default: 8) +/// - height (float): Base height of trapezoid (default: 6) +/// - rows (int): Number of horizontal grid lines (default: 3) +/// - cols (int): Number of vertical grid lines (default: 2) +/// - scale (float): Scale factor for entire chart (default: 0.7) +/// - nasals (bool): Draw schematic nasalized copies near their oral +/// counterparts. For preset languages, this currently adds only the French +/// nasal vowels; for custom strings, only vowels explicitly nasalized in the +/// input are shown in the nasal layer (default: false) +/// - arrows (array): List of (from-vowel, to-vowel) tuples for drawing directed +/// arrows between vowel positions (e.g. diphthongs). Each vowel string accepts +/// tipa-style notation. Unknown vowels are silently skipped. (default: ()) +/// - arrow-color (color): Color for arrow lines and heads (default: black) +/// - arrow-style (string): "solid" or "dashed" line style for arrows (default: "solid") +/// - curved (bool): Curve arrows with a quadratic bezier arc (default: false) +/// - shift (array): List of (vowel, x-offset, y-offset) tuples. Draws a copy of +/// the vowel symbol offset from its canonical trapezoid position by (x, y) in +/// CeTZ canvas units. If the vowel is already plotted, an additional copy is +/// drawn; otherwise it is created. Unknown vowels are silently skipped. (default: ()) +/// - shift-color (color): Color for shifted vowel symbols (default: gray) +/// - shift-size (length, optional): Font size for shifted vowels; none uses the +/// same size as regular vowels (default: none) +/// - highlight (array): List of tipa strings whose background circle is highlighted (default: ()) +/// - highlight-color (color): Circle color for highlighted vowels (default: luma(220)) +/// +/// Returns: CeTZ drawing of IPA vowel chart with positioned vowels +/// +/// Examples: +/// - `#vowels("english", scale: 0.6)` - Plot English vowel inventory +/// - `#vowels("french", scale: 0.6)` - Plot French vowel inventory +/// - `#vowels("aãioõu", nasals: true)` - Add only the nasal vowels marked in the custom inventory +/// - `#vowels("french", nasals: true)` - Add the French nasal vowels +/// - `#vowels("english", arrows: (("a", "U"), ("a", "I"), ("e", "I"), ("O", "I"), ("o", "U")), curved: true)` - Diphthong trajectories +/// +/// Note: Diacritics and non-vowel symbols are ignored during plotting. Nasal +/// overlays are illustrative only and are not language-specific placements. +/// +/// Available languages: english, spanish, portuguese, italian, french, german, +/// japanese, russian, arabic +#let vowels = vowels + +/// Plot consonants on an IPA consonant table +/// +/// Visualizes consonants on the pulmonic IPA consonant chart with proper +/// positioning by place and manner of articulation. Voiceless/voiced pairs +/// are shown left/right in each cell. Impossible articulations are grayed out. +/// +/// Arguments: +/// - consonant-string (string, optional): Consonant symbols to plot, a built-in +/// language name, or tipa-style IPA, passed positionally (e.g. +/// `consonants("ptk")` or `consonants("russian")`). Optional: you may omit it +/// and pass `lang` instead (e.g. `consonants(lang: "russian")`). If both are +/// given and the positional is a language name, the positional wins. +/// - lang (string, optional): Built-in language whose inventory to plot, used when +/// no language-name positional is given (e.g. `consonants(lang: "russian")`). +/// - ui-lang (string): UI label language. Supported aliases: en/english, fr/french, +/// pt/portuguese (default: "en") +/// - affricates (bool): Show affricate row after fricatives (default: false) +/// - aspirated (bool): Show aspirated plosive/affricate rows (default: false) +/// - abbreviate (bool): Use abbreviated place/manner labels (default: false) +/// - simplify (bool): Automatically drop empty rows and columns (default: false) +/// - delete-cols (array): 0-indexed column indices to remove (0=Bilabial ... 10=Glottal) +/// - delete-rows (array): 0-indexed row indices to remove (0=Plosive ... 7=Lateral approximant) +/// - cell-width (float): Width of each cell (default: 1.8) +/// - cell-height (float): Height of each cell (default: 0.9) +/// - label-width (float): Width of row labels (default: 3.5) +/// - label-height (float): Height of column labels (default: 1.2) +/// - scale (float): Scale factor for entire table (default: 0.7) +/// +/// Returns: CeTZ drawing of IPA consonant table with positioned consonants +/// +/// Examples: +/// - `#consonants("italian", affricates: true, abbreviate: true)` - Italian inventory +/// - `#consonants("ts{ts}psS \\*r g{tS} {k \\h}", affricates: true, aspirated: true)` - Custom inventory +/// - `#consonants("english", affricates: true, simplify: true)` - Simplified English inventory +/// - `#consonants("italian", affricates: true, simplify: true, ui-lang: "fr")` - Localized labels +/// +/// Notes: +/// - /w/ (labiovelar) appears in both bilabial and velar columns when /ɰ/ is not present; otherwise only bilabial +/// - Affricates appear in a separate row when affricates: true (displayed without tie bars) +/// - Aspirated consonants appear in separate rows when aspirated: true (e.g., "Plosive (aspirated)") +/// - Both aspirated consonants and affricates must be wrapped with curly brackets: {p \\h} will produce an aspirated p, and {ts} will produce a voiceless alveolar affricate +/// - Diacritics and non-consonant symbols are ignored during plotting +/// +/// Available languages: all, english, spanish, french, german, italian, +/// japanese, portuguese, russian, arabic +#let consonants = consonants + +/// Create an Optimality Theory tableau +/// +/// Generates a formatted OT tableau with candidates, constraints, violations, +/// and shading for irrelevant cells after fatal violations. +/// +/// Arguments: +/// - input (string or content): The input form (can use IPA notation) +/// - candidates (array): Array of candidate forms (strings or content) +/// - constraints (array): Array of constraint names (strings) +/// - violations (array): 2D array of violation strings (use "*" for violations, "!" for fatal) +/// - winner (int): Index of the winning candidate (0-indexed) +/// - dashed-lines (array): Indices of constraints to show with dashed borders (optional) +/// - scale (number, optional): Scale factor for the tableau (default: none) +/// - shade (bool): Whether cells should be shaded after fatal violations (default: true) +/// - prosody-scale (float): Scale factor for prosodic structures in candidates (default: 0.5) +/// - letters (bool): Use letter labels (a, b, c, ...) for candidates (default: false) +/// - gloss (string, optional): Gloss text displayed below the input (default: none) +/// +/// Returns: Table showing OT tableau with winner marked by ☞ +/// +/// Example: +/// ``` +/// #tableau( +/// input: "kraTa", +/// candidates: ("kra.Ta", "ka.Ta", "ka.ra.Ta"), +/// constraints: ("Max", "Dep", "*Complex"), +/// violations: ( +/// ("", "", "*"), +/// ("*!", "", ""), +/// ("", "*!", ""), +/// ), +/// winner: 0, // <- Position of winning cand +/// dashed-lines: (0,) // <- Note the comma +/// ) +/// ``` +#let tableau = tableau + +/// Create a Maximum Entropy (MaxEnt) grammar tableau +/// +/// Generates a MaxEnt tableau showing harmony scores, probabilities, +/// and optional probability visualizations. +/// +/// Arguments: +/// - input (string or content): The input form +/// - candidates (array): Array of candidate forms +/// - constraints (array): Array of constraint names +/// - weights (array): Array of constraint weights (numbers) +/// - violations (array): 2D array of violation counts (numbers) +/// - visualize (bool): Whether to show probability bars (default: true) +/// - sort (bool): Whether to sort candidates by probability, most to least (default: false) +/// - scale (number, optional): Scale factor for the tableau (default: none) +/// - letters (bool): Use letter labels (a, b, c, ...) for candidates (default: false) +/// +/// Returns: Table showing MaxEnt tableau with H(x), P*(x), and P(x) columns +/// +/// Example: +/// ``` +/// #maxent( +/// input: "kraTa", +/// candidates: ("[kra.Ta]", "[ka.Ta]", "[ka.ra.Ta]"), +/// constraints: ("Max", "Dep", "Complex"), +/// weights: (2.5, 1.8, 1), +/// violations: ( +/// (0, 0, 1), +/// (1, 0, 0), +/// (0, 1, 0), +/// ), +/// visualize: true, +/// ) +/// ``` +#let maxent = maxent + +/// Create a Harmonic Grammar (HG) tableau +/// +/// Generates an HG tableau showing harmony scores calculated from +/// weighted constraint violations. HG is deterministic: the candidate +/// with the highest harmony wins. +/// +/// Arguments: +/// - input (string or content): The input form +/// - candidates (array): Array of candidate forms +/// - constraints (array): Array of constraint names +/// - weights (array): Array of constraint weights (numbers) +/// - violations (array): 2D array of violation counts (negative numbers) +/// - scale (number): Optional scale factor (default: auto-scales for >6 constraints) +/// - letters (bool): Use letter labels (a, b, c, ...) for candidates (default: false) +/// +/// Returns: Table showing HG tableau with constraint weights and h(y) harmony column +/// +/// Example: +/// ``` +/// #hg( +/// input: "kraTa", +/// candidates: ("[kra.Ta]", "[ka.Ta]", "[ka.ra.Ta]"), +/// constraints: ("Max", "Dep", "*Complex"), +/// weights: (2.5, 1.8, 1), +/// violations: ( +/// (0, 0, -1), +/// (-1, 0, 0), +/// (0, -1, 0), +/// ), +/// ) +/// ``` +/// +/// Note: h(y) = Σ(weight × violation). Candidate with highest (least negative) harmony wins. +#let hg = hg + +/// Create a Noisy Harmonic Grammar (NHG) tableau with symbolic noise +/// +/// Pedagogical version showing noise as symbolic formulas (e.g., "-n₁"). +/// Useful for teaching how noise affects harmony calculations. +/// +/// Arguments: +/// - input (string or content): The input form +/// - candidates (array): Array of candidate forms +/// - constraints (array): Array of constraint names +/// - weights (array): Array of constraint weights +/// - violations (array): 2D array of violation counts (negative numbers) +/// - probabilities (array): Optional array of probability values to display +/// - scale (number): Scale factor (default: auto-scales for >6 constraints) +/// - letters (bool): Use letter labels (a, b, c, ...) for candidates (default: false) +/// +/// Returns: Table with h(y), ε(y) (symbolic), and optional P(y) columns +/// +/// Example: +/// ``` +/// #nhg-demo( +/// input: "kraTa", +/// candidates: ("[kra.Ta]", "[ka.Ta]", "[ka.ra.Ta]"), +/// constraints: ("Max", "Dep", "*Complex"), +/// weights: (2.5, 1.8, 1), +/// violations: ( +/// (0, 0, -1), +/// (-1, 0, 0), +/// (0, -1, 0), +/// ), +/// probabilities: (0.673, 0.08, 0.247), +///) +/// ``` +#let nhg-demo = nhg-demo + +/// Create a Noisy Harmonic Grammar (NHG) tableau with Monte Carlo simulation +/// +/// Samples noise from N(0,1), calculates probabilities via simulation. +/// Noise is added to constraint weights, and the candidate with highest +/// noisy harmony wins each trial. +/// +/// Arguments: +/// - input (string or content): The input form +/// - candidates (array): Array of candidate forms +/// - constraints (array): Array of constraint names +/// - weights (array): Array of constraint weights +/// - violations (array): 2D array of violation counts (negative numbers) +/// - num-simulations (int): Number of Monte Carlo trials (default: 1000) +/// - seed (int, optional): Random seed for reproducibility (default: none) +/// - show-epsilon (bool): Whether to show epsilon column (default: true) +/// - scale (number): Scale factor (default: auto-scales for >6 constraints) +/// - letters (bool): Use letter labels (a, b, c, ...) for candidates (default: false) +/// +/// Returns: Table with h(y), optional ε(y) (one sample), and P(y) (from simulation) +/// +/// Example: +/// ``` +/// #nhg( +/// input: "kraTa", +/// candidates: ("[kra.Ta]", "[ka.Ta]", "[ka.ra.Ta]"), +/// constraints: ("Max", "Dep", "*Complex"), +/// weights: (2.5, 1.8, 1), +/// violations: ( +/// (0, 0, -1), +/// (-1, 0, 0), +/// (0, -1, 0), +/// ), +/// ) +/// ``` +/// +/// Note: Probabilities are estimated empirically. More simulations = more accurate +/// but slower compilation. Default 1000 is usually sufficient. +#let nhg = nhg + +/// Create a Hasse diagram for Optimality Theory constraint rankings +/// +/// Generates a visual representation of the partial order of constraint rankings. +/// +/// Features: +/// - Supports partial orders (not all constraints need to be ranked) +/// - Handles floating constraints with no ranking relationships +/// - Supports dashed and dotted line styles for different edge types +/// - Auto-scales for complex hierarchies +/// +/// Arguments: +/// - rankings (array): Array of tuples representing rankings: +/// - Three-element tuple `(A, B, level)` means A dominates B, and A is at stratum `level` (REQUIRED) +/// - Four-element tuple `(A, B, level, style)` means A dominates B, A at stratum `level`, with line `style` +/// - Single-element tuple `(A,)` means A is floating (no ranking) +/// - Line styles: "solid" (default), "dashed", "dotted" +/// - Note: Level specification is REQUIRED for all edges to ensure proper stratification +/// - scale (number or auto): Scale factor for diagram (default: auto-scales based on complexity) +/// - node-spacing (number): Horizontal spacing between nodes (default: 2.5) +/// - level-spacing (number): Vertical spacing between levels (default: 1.5) +/// +/// Returns: A Hasse diagram showing the constraint hierarchy +/// +/// Examples: +/// ``` +/// // Basic scenario +/// #hasse( +/// ( +/// ("*Complex", "Max", 0), +/// ("*Complex", "Dep", 0), +/// ("Onset", "Max", 0), +/// ("Onset", "Dep", 0), +/// ("Max", "NoCoda", 1), +/// ("Dep", "NoCoda", 1), +/// ), +/// scale: 0.9 +/// ) +/// ``` +#let hasse = hasse + +// SPE/Feature function +/// Create a feature matrix in SPE notation +/// +/// Displays phonological features in a vertical matrix with square brackets, +/// commonly used in Sound Pattern of English (SPE) style representations. +/// +/// Arguments: +/// - ..args: Features as separate arguments or comma-separated string +/// +/// Returns: Mathematical vector notation with features +/// +/// Examples: +/// - `#feat("+cons", "-son", "+voice")` - Three features as separate args +/// - `#feat("+cons,-son,+voice")` - Three features as comma-separated string +#let feat = feat + +/// Display complete distinctive feature matrix for an IPA segment +/// +/// Takes an IPA symbol and displays its complete distinctive feature specification +/// from Hayes (2009) Introductory Phonology. Features are shown in SPE-style +/// vertical matrix notation. +/// +/// Arguments: +/// - segment (string): IPA symbol using Unicode or tipa-style notation +/// - all (bool): Show all features including unspecified (0) values (default: false) +/// - ui-lang (string): UI label language. Supported aliases: en/english, fr/french, +/// pt/portuguese (default: "en") +/// +/// Returns: Complete feature matrix in SPE notation +/// +/// Examples: +/// - `#feat-matrix("p")` - Shows feature matrix for /p/ +/// - `#feat-matrix("\\ae")` - Shows feature matrix for /æ/ +/// - `#feat-matrix("\\t tS")` - Affricate using tipa-inspired notation +/// - `#feat-matrix("i", all: true)` - Shows all features including 0 values +/// +/// Note: Based on Hayes (2009) feature system. Includes manner, laryngeal, +/// and place features for consonants; syllabic, height, backness, and rounding +/// features for vowels. +#let feat-matrix = feat-matrix + +/// Create an autosegmental representation +/// +/// Generates an autosegmental representation visualizing features or tones +/// on a separate tier from segments. Supports spreading (one-to-many associations), +/// delinking, multiple linking, and floating features/tones. Ideal for illustrating +/// phonological processes like tone spreading, vowel harmony, or feature geometry. +/// +/// Arguments: +/// - segments (array): Segment strings (use "" for empty timing slots) +/// - features (array): Feature/tone labels corresponding to segments (use "" for no association) +/// - links (array): Tuples of (feature-index, segment-index) for association lines (default: ()) +/// - delinks (array): Tuples of (feature-index, segment-index) for delinking marks (default: ()) +/// - spacing (float): Horizontal spacing between segments (default: 1.5) +/// - arrow (bool): Show arrow between representations (for process diagrams) (default: false) +/// - tone (bool): Whether the representation shows tones vs features (default: false) +/// - highlight (array): Indices of segments to highlight with background color (default: ()) +/// - float (array): Indices of floating (unassociated) features/tones (default: ()) +/// - multilinks (array): Tuples of (feature-index, (seg1, seg2, ...)) for one-to-many links (default: ()) +/// - baseline (ratio): Vertical alignment of the box (default: 40%) +/// - gloss (string): Optional gloss text below baseline (default: "") +/// - dash (string): Line style for dashed association lines (default: "dashed") +/// +/// Returns: Autosegmental representation +/// +/// Examples: +/// ``` +/// // Basic tone spreading: L tone spreads to multiple syllables +/// #autoseg( +/// ("a", "", "g", "a", "f", "i"), +/// features: ("L", "", "", "H", "", ""), +/// links: ((0, 3),), +/// delinks: ((3, 3),), +/// tone: true, +/// spacing: 0.5, +/// multilinks: ((3, (3, 5)),), +/// ) +/// +/// // Feature spreading with arrow (showing phonological process) +/// #autoseg( +/// ("t", "a", "n"), +/// features: ("+nasal", "", ""), +/// links: ((0, 0),), +/// arrow: true, +/// ) +/// +/// // Floating tone with highlighting +/// #autoseg( +/// ("m", "a", "m", "a"), +/// features: ("H", "L", "", ""), +/// float: (0,), +/// highlight: (1, 3), +/// tone: true, +/// ) +/// ``` +/// +/// Note: Index numbering is 0-based. Use empty strings "" in segments or features +/// arrays to create timing slots without content or features without associations. +#let autoseg = autoseg + +/// Create a multi-tier phonological representation +/// +/// Draws N-tier diagrams for CV phonology, skeletal tier structures, and other +/// multi-level representations. Each tier is a row of labels connected by +/// association lines. Supports auto-linking, floating elements, highlighting, +/// dashed lines, and delinking marks. +/// +/// Arguments: +/// - levels (array): Array of arrays; each inner array is a tier of label strings (use "" for empty positions). +/// Entries can be "label", ("label", col) for fractional columns, or ("label", col, level) for fractional levels. +/// - links (array): Extra solid lines as either `((level1, col1), (level2, col2))` +/// tuples or `("name1", "name2")` pairs (default: ()) +/// - dashed (array): Dashed lines as either coordinate tuples or string-name pairs (default: ()) +/// - delinks (array): Cross marks on connections as either coordinate tuples or string-name pairs (default: ()) +/// - arrows (array): Rectangular-path arrows as either coordinate tuples or string-name pairs (default: ()). +/// Top-level arrows arc above; bottom-level arrows arc below. Arrowhead at destination. +/// - arrow-delinks (array): Indices of arrows that should have a delink mark (||) at the midpoint (default: ()) +/// - float (array): Positions excluded from auto-linking as `(level, col)` tuples or `"name"` strings (default: ()) +/// - highlight (array): Positions with circle highlight as `(level, col)` tuples or `"name"` strings (default: ()) +/// - ipa (array): Level indices whose labels should be rendered as IPA (default: ()) +/// - tier-labels (array): Labels for tiers as (level, "label") tuples, placed to the right (default: ()) +/// - spacing (float): Horizontal spacing between columns (default: 1.5) +/// - level-spacing (float): Vertical spacing between tiers (default: 1.2) +/// - stroke-width (length): Line thickness (default: 0.05em) +/// - baseline (string): Vertical alignment (default: 40%) +/// - scale (float): Uniform scale factor (default: 1.0) +/// - show-grid (bool): Show background grid for debugging layout (default: false) +/// - show-refs (bool): Show node references below nodes for debugging; string nodes use generated names and content nodes fall back to coordinates (default: false) +/// +/// Returns: Multi-tier phonological representation +/// +/// Example: +/// ``` +/// // From Goad (2012) +/// #multi-tier( +/// levels: ( +/// ("O", "R", "", "O", "R", "O", "R"), +/// ("", "N1", "", "", "N2", "", "N3"), +/// ("", "x", "x", "x", "x", "x", "x"), +/// ("", "", "s", "t", "E", "m", ""), +/// ), +/// links: ( +/// ("r2", "x2"), +/// ), +/// ipa: (3,), +/// arrows: ( +/// ("t1", "s1"), +/// ("r2", "r1"), +/// ), +/// arrow-delinks: ( +/// (1,) +/// ), +/// spacing: 1, +/// ) +/// ``` +/// +/// Note: Trailing digits in labels are automatically rendered as subscripts +/// (e.g., "O1" becomes O₁). Standalone "x" is rendered as "×" (multiplication sign). +/// Non-empty labels also receive automatic reference names in reading order +/// (e.g., `sigma1`, `sigma2`, `x3`), which can be used in `links`, `dashed`, +/// `delinks`, `float`, `highlight`, and `arrows`. +#let multi-tier = multi-tier + +/// Create free-positioned sound-shift diagrams +/// +/// Draws IPA labels at arbitrary 2D positions and connects them with arrows. +/// This is useful for historical or schematic shift diagrams that are awkward +/// to represent in an IPA trapezoid. +/// +/// Arguments: +/// - nodes (array): Array of dictionaries describing nodes. Each node needs +/// `at: (x, y)` and `label:`. When `label` is a string, it also serves as +/// the node identifier by default. Use `id:` only when labels are duplicated +/// or when the visible label should differ from the reference key. +/// - arrows (array): Array of arrow specs. Each arrow can be a `(from, to)` +/// tuple or a dictionary with `from:` and `to:`. Endpoints can be node ids +/// or raw coordinate pairs. +/// - highlights (array): Node ids or coordinate pairs to highlight with a +/// background circle (default: ()) +/// - node-size (length): Default IPA font size for nodes (default: 2.2em) +/// - text-fill (color): Default node color (default: black) +/// - highlight-fill (fill): Default node/highlight fill (default: `luma(230)`) +/// - highlight-radius (float): Default circle radius in canvas units (default: 0.42) +/// - arrow-color (color): Default arrow color (default: black) +/// - arrow-style (str): Default arrow style: `"solid"`, `"dashed"`, or `"dotted"` +/// - arrow-width (length): Default arrow stroke width (default: 0.8pt) +/// - arrow-size (float): Default arrowhead scale (default: 1.0) +/// - curved (bool): Curve arrows by default (default: false) +/// - curve (float): Default curvature multiplier for curved arrows (default: 0.45) +/// - scale (float): Uniform diagram scale factor (default: 1.0) +/// +/// Returns: A CeTZ-based sound-shift diagram +/// +/// Example: +/// ``` +/// #sound-shift( +/// nodes: ( +/// (label: "I", at: (-4.2, 2.8)), +/// (label: "E", at: (-1.9, 1.0)), +/// (label: "2", at: (0.9, 1.0)), +/// (label: "O", at: (3.8, 1.0)), +/// (label: "A", at: (2.4, -1.5)), +/// (label: "\\ae", at: (-1.0, -1.5)), +/// ), +/// arrows: ( +/// ("E", "2"), +/// ("2", "O"), +/// ("O", "A"), +/// ("A", "\\ae"), +/// ("I", "E"), +/// (from: "\\ae", to: "I", curved: true, curve: 0.28), +/// ), +/// scale: 0.7, +/// ) +/// ``` +#let sound-shift = sound-shift + +/// Create a numbered linguistic example +/// +/// Generates numbered examples (1), (2), etc. similar to linguex in LaTeX. +/// Wrap content directly for a single example, or use list syntax for +/// automatically lettered sub-examples. Use `labels` to make individual +/// sub-examples referenceable. +/// +/// Arguments: +/// - body (content): The example content +/// - number-dy (length): Vertical offset for the number (optional; default: 0.4em) +/// - caption (string): Caption for outline (hidden in document; optional) +/// - title (string, optional): Title for the example (default: none) +/// - labels (array): Array of labels for sub-examples (default: ()) +/// - columns (array): Column widths for data columns (default: ()) +/// +/// Returns: Numbered example that can be labeled and referenced +/// +/// Example: +/// ``` +/// #ex(caption: "A phonology example", labels: (, ), columns: (5em, 2em, 5em))[ +/// - #ipa("/anba/") & #a-r & #ipa("[amba]") +/// - #ipa("/anka/") & #a-r & #ipa("[aNka]") +/// ] +/// ``` +#let ex = ex + +/// Show rules for linguistic examples +/// +/// Apply this to enable proper reference formatting for ex(). +/// References render as (1), (1a), (1b), etc. +/// +/// Usage: `#show: ex-rules` +#let ex-rules = ex-rules + +/// Arrow symbols for phonological rules and processes +/// +/// Convenience symbols for showing derivations, mappings, and processes. +/// All arrows use New Computer Modern font for consistent styling. +/// +/// Available arrows: +/// - `#a-r` → right arrow +/// - `#a-l` ← left arrow +/// - `#a-u` ↑ up arrow +/// - `#a-d` ↓ down arrow +/// - `#a-lr` ↔ bidirectional arrow +/// - `#a-ud` ↕ vertical bidirectional arrow +/// - `#a-sr` ↝ squiggly right arrow +/// - `#a-sl` ↜ squiggly left arrow +/// - `#a-r-large` → large right arrow with horizontal spacing +/// +/// Example: `#ipa("/anba/") #a-r #ipa("[amba]")` produces /anba/ → [amba] +#let a-r = a-r +#let a-l = a-l +#let a-u = a-u +#let a-d = a-d +#let a-lr = a-lr +#let a-ud = a-ud +#let a-sr = a-sr +#let a-sl = a-sl +#let a-r-large = a-r-large + +/// Upright Greek symbols for phonological notation +/// +/// Convenience bindings for commonly used Greek letters in phonology. +/// These render upright in text mode (unlike math-mode `$sigma$` which italicizes). +/// +/// Lowercase: +/// - `#alpha` α, `#beta` β, `#gamma` γ, `#delta` δ +/// - `#lambda` λ, `#mu` μ, `#phi` φ, `#pi` π +/// - `#sigma` σ, `#tau` τ, `#omega` ω +/// +/// Uppercase: +/// - `#cap-sigma` Σ (foot), `#cap-phi` Φ (phonological phrase), `#cap-omega` Ω (utterance) +/// +/// Example: `The syllable #sigma contains an onset and a rhyme.` +#let alpha = alpha +#let beta = beta +#let gamma = gamma +#let delta = delta +#let lambda = lambda +#let mu = mu +#let phi = phi +#let pi = pi +#let sigma = sigma +#let tau = tau +#let omega = omega +#let cap-phi = cap-phi +#let cap-sigma = cap-sigma +#let cap-omega = cap-omega + +/// Create an underline blank for fill-in exercises or SPE rules +/// +/// Generates a horizontal line (underline) useful for worksheets, +/// exercises, or indicating missing/redacted content. +/// +/// Arguments: +/// - width (length): Width of the blank line (default: 2em) +/// +/// Returns: A box with bottom stroke +/// +/// Example: `The word #blank() means "house".` +#let blank = blank + +/// Mark extrametrical content with angle brackets +/// +/// Wraps content in ⟨angle brackets⟩ to indicate extrametricality +/// in metrical phonology representations. +/// +/// Arguments: +/// - content: The content to mark as extrametrical +/// +/// Returns: Content wrapped in ⟨⟩ +/// +/// Example: `#extra[tion]` produces ⟨tion⟩ +#let extra = extra + +/// Place a ToBI intonation label above the current inline text position +/// +/// Designed to be placed inline immediately after the syllable or word being annotated. +/// The label floats above the text at the insertion point, optionally connected by +/// a vertical stem. +/// +/// Arguments: +/// - label (string): ToBI label, e.g., "*L", "H%", "L+H*", "!H*" +/// - line (boolean): draw a vertical stem connecting label to text (default: true) +/// - height (length): stem length — controls how far above the text the label sits (default: 2em) +/// - lift (length): gap between stem bottom and text baseline (default: 0.8em) +/// - gap (length): horizontal gap around the annotation (default: 0.22em) +/// - en-dash (bool): render dashes as en-dashes instead of non-breaking hyphens (default: true) +/// +/// Example: +/// ``` +/// You're a we#int("*L")rewolf?#h(2em)#int("H%", line: false) +/// ``` +#let int = int + +/// Draw a feature-geometry tree for a consonant or vocoid +/// +/// Produces a hierarchical diagram following Clements & Hume (1995) +/// or Sagey (1986). All feature nodes are optional; parent nodes are inferred +/// automatically from their children. +/// +/// Use `ph` to load a built-in segment preset and optionally override individual +/// features. The preset label (e.g. "/i/") is shown above the root unless +/// `segment` is given. +/// +/// Arguments: +/// - ph (str): Segment preset key in tipa-style notation. Supported segments +/// include common vowels (a e i o u E O I U y W 7 \o \oe 2 A 6 @ 1 0 \ae) +/// and consonants (p b t d k g f v s z S Z n m N \N j h ? T D x G F B V M +/// \:t \:d \:s \:z \:n r l J C \T). Wrap in slashes/brackets to override the +/// auto segment label: `ph: "/a/"`. +/// - model (str): `"ch"` (default) for Clements & Hume 1995 (aperture nodes for +/// height); `"sagey"` for Sagey 1986 (dorsal sub-features for height/backness, +/// `[round]` under labial). Affects preset vowels only; consonant presets are +/// identical in both models. +/// - ui-lang (string): UI label language. Supported aliases: en/english, fr/french, +/// pt/portuguese (default: "en") +/// - root (array): Root matrix features, e.g. `("+son", "-vocoid")`. +/// - laryngeal (bool): Show "laryngeal" class node explicitly. +/// - nasal (bool, str): `[nasal]`. Values: `true` → `[nasal]`, +/// `"+"` → `[+nasal]`, `"-"` → `[−nasal]`. +/// - spread (bool): `[spread glottis]` under laryngeal. +/// - constricted (bool): `[constricted glottis]` under laryngeal. +/// - voice (bool, str): `[voice]` under laryngeal. Same sign convention as `nasal`. +/// - continuant (bool, str): `[continuant]` under oral cavity. Same sign convention. +/// Pass an array of two values for affricates: `continuant: ("-", "+")`. +/// - labial (bool, array): `[labial]`. Array adds sub-features: +/// `labial: ("round",)`. +/// - coronal (bool, array): `[coronal]`. Array replaces `anterior`/`distributed`: +/// `coronal: ("+ant", "-distr")`. +/// - anterior (bool, str): `[anterior]` under coronal. Same sign convention. +/// - distributed (bool): `[distributed]` under coronal. +/// - dorsal (bool, array): `[dorsal]`. Array adds sub-features (Sagey-style): +/// `dorsal: ("+hi", "-back")`. +/// - radical (bool): `[rad]` (pharyngeal/radical place). +/// - vocalic (bool): Show "vocalic" class node (vocoid branch). +/// - vplace (bool): Show "V-place" under vocalic. Inferred automatically when +/// vocalic is active and any place feature is supplied. +/// - aperture (bool, array): "aperture" node under vocalic (CH model). Array of +/// up to 3 values controls `[open1]`/`[open2]`/`[open3]`: +/// `aperture: ("-", "+", "-")` → close-mid height. +/// - tense (bool, str): `[tense]` under vocalic. Same sign convention as `nasal`. +/// - scale (number): Uniform scale factor (default: 1.0). +/// - position (array): Manual layout tweaks. Each entry: `("node-key", dx, dy)`. +/// Node keys are bare argument names, e.g. `"continuant"`, `"oral-cavity"`. +/// - delinks (array): Node keys whose line to their parent is replaced with a +/// delink mark, e.g. `delinks: ("c-place",)`. +/// - segment (content): Label shown above root. Defaults to the `ph` value +/// wrapped in slashes when `ph` is set. +/// - prefix (string): Prefix text before the segment label (default: "") +/// - suffix (string): Suffix text after the segment label (default: "") +/// - highlight (array): Node names to highlight; all others are dimmed. +/// - timing (auto, false, array, or string): Timing tier specification. `auto` infers +/// from `ph` (e.g., long vowels get two timing slots), `false` hides the tier (default: auto) +/// +/// Returns: CeTZ drawing of the feature-geometry tree +/// +/// Examples: +/// ``` +/// // Preset segment +/// #geom(ph: "i") +/// +/// // Manual consonant: voiceless alveolar stop +/// #geom(root: ("-son", "-approx", "-vocoid"), +/// coronal: true, anterior: "+", voice: "-", continuant: "-", +/// segment: "/t/") +/// +/// // Sagey-model vowel /y/ +/// #geom(ph: "y", model: "sagey", scale: 1.2) +/// ``` +#let geom = geom + +/// Draw two or more feature-geometry trees side by side with optional inter-tree arrows +/// +/// Each tree is specified as a spec dict (the same keys as `geom()`, all optional). +/// The `model` and `scale` parameters apply uniformly to all trees. +/// +/// Cross-tree arrows connect nodes by their anchor names. Node names are formed +/// by lowercasing the argument name, replacing spaces with hyphens, and appending +/// the 1-based tree index: `"labial1"`, `"oral-cavity2"`, `"c-place1"`. +/// +/// Arguments: +/// - ..trees (arguments): Positional spec dicts, one per tree. Each may include +/// a `ph` key to load a preset, plus any `geom()` keys to override features. +/// A per-tree `scale` key (number) scales that tree's coordinates and font +/// size relative to the group `scale`. +/// - arrows (array): Cross-tree arrows. Each entry is either `(from, to)` or a +/// dict `(from: str, to: str, color: color, ctrl: (number, number))`. +/// - `ctrl`: two Y-lifts `(lift1, lift2)`, one per endpoint. Positive lifts +/// the departure upward; negative dips the arrival below the target. +/// Overrides `curved` when set. +/// All keys except `from`/`to` are optional. +/// - gap (number): Canvas-unit gap between trees (default: 1.5). +/// - scale (number): Uniform scale factor for the whole group (default: 1.0). +/// - ui-lang (string): UI label language. Supported aliases: en/english, fr/french, +/// pt/portuguese (default: "en") +/// - model (str): `"ch"` (default) or `"sagey"`. Applies to all trees. +/// - position (array): Layout tweaks. Each entry: `("node-key-with-index", dx, dy)`, +/// e.g. `("continuant1", -0.2, 0.3)`. Arrows follow the adjusted positions. +/// - delinks (array): Node anchor names (with tree index) whose parent line is +/// replaced with a delink mark, e.g. `delinks: ("c-place1",)`. +/// - curved (bool): Draw arrows as obstacle-avoiding Bézier curves (default: false). +/// - highlight (array): Node anchor names to highlight; all others are dimmed. +/// +/// Returns: CeTZ drawing of all trees in one canvas +/// +/// Example: +/// ``` +/// // Spreading: nasal spreading from n to a +/// #geom-group( +/// (ph: "a"), +/// (ph: "n"), +/// arrows: ((from: "nasal2", to: "root1", ctrl: (1.1, -1.5)),), +/// curved: true, +/// ) +/// ``` +#let geom-group = geom-group diff --git a/packages/preview/phonokit/0.5.11/multi-tier.typ b/packages/preview/phonokit/0.5.11/multi-tier.typ new file mode 100644 index 0000000000..fe97fac79b --- /dev/null +++ b/packages/preview/phonokit/0.5.11/multi-tier.typ @@ -0,0 +1,510 @@ +// Multi-tier phonological representations (CV phonology, skeletal tiers, etc.) +// Part of phonokit package + +#import "@preview/cetz:0.5.2" +#import "_config.typ": phonokit-font +#import "ipa.typ": ipa as ipa-convert + +#let multi-tier( + levels: (), + links: (), + dashed: (), + delinks: (), + arrows: (), + arrow-delinks: (), + float: (), + highlight: (), + ipa: (), + tier-labels: (), + spacing: 1.5, + level-spacing: 1.2, + stroke-width: 0.05em, + baseline: 40%, + scale: 1.0, + show-grid: false, + show-refs: false, +) = { + // Validate input + assert(type(levels) == array, message: "levels must be an array of arrays") + assert(levels.len() > 0, message: "levels array cannot be empty") + + // Convert labels: "x" → "×" (skeletal slot), Greek names → Unicode, trailing digits → subscripts + let greek-map = ( + "sigma": "σ", + "Sigma": "Σ", + "mu": "μ", + "omega": "ω", + "Omega": "Ω", + "beta": "β", + "alpha": "α", + "gamma": "γ", + "delta": "δ", + "phi": "φ", + "Phi": "Φ", + "pi": "π", + "tau": "τ", + "lambda": "λ", + ) + + let render-label(label) = { + // Greek letter substitution (applied before digit subscripting so "sigma1" → "σ₁") + let m-greek = label.match(regex("^([A-Za-z]+?)(\d*)$")) + if m-greek != none { + let base = m-greek.captures.at(0) + let trail = m-greek.captures.at(1) + if base in greek-map { + label = greek-map.at(base) + trail + } + } + + let label = label.replace(regex("^x$"), "×") + + let m = label.match(regex("^(.*?)(\d+)$")) + if m != none { + let base = m.captures.at(0) + let digits = m.captures.at(1) + let subscript-map = ( + "0": "₀", + "1": "₁", + "2": "₂", + "3": "₃", + "4": "₄", + "5": "₅", + "6": "₆", + "7": "₇", + "8": "₈", + "9": "₉", + ) + let sub = digits.clusters().map(d => subscript-map.at(d, default: d)).join() + base + sub + } else { + label + } + } + + // Normalize a visible label into an automatic reference stem. + // Examples: "N1" -> "n1", "C'" -> "cbar", "sigma" -> "sigma". + let ref-stem(label) = { + if type(label) != str { return none } + + let stem = lower(label.trim()) + if stem == "" { return none } + + stem = stem.replace("'", "bar").replace("\u{2019}", "bar") + stem = stem.replace(regex("[^a-z0-9]+"), "-") + stem = stem.replace(regex("^-+"), "") + stem = stem.replace(regex("-+$"), "") + + if stem == "" { none } else { stem } + } + + // Parse levels into a grid of (label, x-position, y-level) tuples + // Entries can be: + // "label" → label at (array col index, array level index) + // ("label", col) → fractional column, normal level + // ("label", col, level) → fractional column, fractional level + // "" → empty slot + let parsed-grid = levels + .enumerate() + .map(((level-idx, row)) => { + row + .enumerate() + .map(((col-idx, entry)) => { + if type(entry) == array { + let col-pos = entry.at(1) + let level-pos = entry.at(2, default: level-idx) + (label: entry.at(0), col-x: col-pos, level-pos: level-pos) + } else { + (label: entry, col-x: col-idx, level-pos: level-idx) + } + }) + }) + + let ref-counts = (:) + let name-to-node = (:) + let tier-grid = () + + for (level-idx, row) in parsed-grid.enumerate() { + let tier-row = () + for (col-idx, cell) in row.enumerate() { + let stem = ref-stem(cell.label) + let ref-name = if cell.label == "" or stem == none { + none + } else { + let count = ref-counts.at(stem, default: 0) + 1 + ref-counts.insert(stem, count) + let key = stem + str(count) + name-to-node.insert(key, (level-idx, col-idx)) + key + } + + tier-row.push((..cell, ref-name: ref-name)) + } + tier-grid.push(tier-row) + } + + let resolve-node-ref(ref, arg-name: "reference") = { + if type(ref) == str { + assert(ref in name-to-node, message: arg-name + " references unknown multi-tier node `" + ref + "`") + name-to-node.at(ref) + } else { + assert(type(ref) == array and ref.len() == 2, message: arg-name + " node references must be `(level, col)` or a string key") + (ref.at(0), ref.at(1)) + } + } + + let resolve-link-ref(link, arg-name: "reference") = { + assert(type(link) == array and link.len() == 2, message: arg-name + " entries must contain exactly two node references") + ( + resolve-node-ref(link.at(0), arg-name: arg-name), + resolve-node-ref(link.at(1), arg-name: arg-name), + ) + } + + let resolved-float = float.map(item => resolve-node-ref(item, arg-name: "float")) + let resolved-highlight = highlight.map(item => resolve-node-ref(item, arg-name: "highlight")) + let resolved-links = links.map(item => resolve-link-ref(item, arg-name: "links")) + let resolved-dashed = dashed.map(item => resolve-link-ref(item, arg-name: "dashed")) + let resolved-delinks = delinks.map(item => resolve-link-ref(item, arg-name: "delinks")) + let resolved-arrows = arrows.map(item => resolve-link-ref(item, arg-name: "arrows")) + + let num-levels = tier-grid.len() + + // Find the rightmost column x-position across all levels (for tier label placement) + let max-col-x = calc.max(..tier-grid.map(row => calc.max(..row.map(cell => cell.col-x)))) + + // Bind parameters to avoid shadowing built-in/imported names inside CeTZ closure + let scale-factor = scale + let sw = stroke-width * scale-factor + let ipa-levels = ipa + let refs-visible = show-refs + + // Vertical offsets from level center (following prosody.typ pattern) + let text-above = 0.30 + let text-below = -0.58 + let line-top = 0.42 + let line-bot = -0.18 + + box(inset: 1.2em, baseline: baseline, cetz.canvas(length: scale-factor * 1cm, { + import cetz.draw: * + + let debug-ref(level, col, cell) = { + if cell.ref-name != none { + cell.ref-name + } else if cell.label != "" { + "(" + str(level) + ", " + str(col) + ")" + } else { + none + } + } + + // Helper: y-coordinate for a level index (used for default positioning) + let level-y(level) = -level * level-spacing + + // Helper: get a cell's actual position (respects fractional col and level overrides) + let cell-x(level, col) = tier-grid.at(level).at(col).col-x * spacing + let cell-y(level, col) = -tier-grid.at(level).at(col).level-pos * level-spacing + + // Helper: line departure point (bottom of label) + let bot-point(level, col) = { + (cell-x(level, col), cell-y(level, col) + line-bot) + } + + // Helper: line arrival point (top of label) + let top-point(level, col) = { + (cell-x(level, col), cell-y(level, col) + line-top) + } + + // Determine line attachment points for a pair of positions + let line-endpoints(l1, c1, l2, c2) = { + if l1 == l2 { + (bot-point(l1, c1), bot-point(l2, c2)) + } else if l1 < l2 { + (bot-point(l1, c1), top-point(l2, c2)) + } else { + (bot-point(l2, c2), top-point(l1, c1)) + } + } + + // === Layer 0: Debug grid (behind everything) === + if show-grid { + let grid-color = luma(180) + let grid-stroke = (paint: grid-color, thickness: 0.3pt, dash: "dashed") + + let max-col = int(max-col-x) + 1 + let pad = 0.6 + + // Vertical lines for each integer column + for col in range(max-col) { + let x = col * spacing + line( + (x, pad), + (x, -(num-levels - 1) * level-spacing - pad), + stroke: grid-stroke, + ) + } + + // Horizontal lines for each level + for level-i in range(num-levels) { + let y = level-y(level-i) + line( + (-pad, y), + ((max-col - 1) * spacing + pad, y), + stroke: grid-stroke, + ) + } + + // Column index labels (above the diagram) + for col in range(max-col) { + content( + (col * spacing, pad + 0.3), + text(size: 0.9em * scale-factor, fill: grid-color, font: "Courier New", str(col)), + ) + } + + // Level index labels (left of the diagram) + for level-i in range(num-levels) { + content( + (-pad - 0.4, level-y(level-i)), + text(size: 0.9em * scale-factor, fill: grid-color, font: "Courier New", str(level-i)), + ) + } + + // Column index labels (below the diagram) + for col in range(max-col) { + content( + (col * spacing, -(num-levels - 1) * level-spacing - pad - 0.3), + text(size: 0.9em * scale-factor, fill: grid-color, font: "Courier New", str(col)), + ) + } + + // Level index labels (right of the diagram) + for level-i in range(num-levels) { + content( + ((max-col - 1) * spacing + pad + 0.4, level-y(level-i)), + text(size: 0.9em * scale-factor, fill: grid-color, font: "Courier New", str(level-i)), + ) + } + + // Dots at every grid intersection + for level-i in range(num-levels) { + for col in range(max-col) { + circle( + (col * spacing, level-y(level-i)), + radius: 0.05, + fill: grid-color, + stroke: none, + ) + } + } + } + + // === Layer 1: Auto-link lines (between adjacent-level same-column non-empty cells) === + for level in range(num-levels - 1) { + let row-top = tier-grid.at(level) + let row-bot = tier-grid.at(level + 1) + let cols = calc.min(row-top.len(), row-bot.len()) + + for col in range(cols) { + if row-top.at(col).label == "" or row-bot.at(col).label == "" { continue } + if (level, col) in resolved-float or (level + 1, col) in resolved-float { continue } + + let (p1, p2) = line-endpoints(level, col, level + 1, col) + line(p1, p2, stroke: sw) + } + } + + // === Layer 2: Extra solid links (skip any that also appear in dashed) === + for link in resolved-links { + if link in resolved-dashed { continue } + let ((l1, c1), (l2, c2)) = link + let (p1, p2) = line-endpoints(l1, c1, l2, c2) + line(p1, p2, stroke: sw) + } + + // === Layer 3: Dashed lines === + for d in resolved-dashed { + let ((l1, c1), (l2, c2)) = d + let (p1, p2) = line-endpoints(l1, c1, l2, c2) + line(p1, p2, stroke: (dash: "dashed", thickness: sw)) + } + + // === Layer 4: Highlight circles (drawn behind labels, centered on text) === + for (level-idx, col-idx) in resolved-highlight { + let cell = tier-grid.at(level-idx).at(col-idx) + if cell.label == "" { continue } + + let x = cell-x(level-idx, col-idx) + let y = cell-y(level-idx, col-idx) + text-above / 3 + + // White mask circle (slightly larger, masks lines behind it) + circle((x, y), radius: 0.45, stroke: none, fill: white) + // Visible highlight circle + circle((x, y), radius: 0.35, stroke: sw, fill: none) + } + + // === Layer 5: Labels (identical rendering regardless of highlight) === + for (level-idx, row) in tier-grid.enumerate() { + for (col-idx, cell) in row.enumerate() { + if cell.label == "" { continue } + + let x = cell-x(level-idx, col-idx) + let y = cell-y(level-idx, col-idx) + text-above + + let is-ipa = level-idx in ipa-levels + + let label-content = if is-ipa { + context text(font: phonokit-font.get(), ipa-convert(cell.label)) + } else if type(cell.label) == str { + let rendered = render-label(cell.label) + context text(font: phonokit-font.get(), rendered) + } else { + context text(font: phonokit-font.get(), cell.label) + } + + content( + (x, y), + anchor: "north", + text(size: 1em * scale-factor, box( + inset: 0.15em, + align(center + horizon, label-content), + )), + ) + } + } + + // === Layer 5: Tier labels (right-aligned, to the right of the diagram) === + for tl in tier-labels { + let (level-idx, label-text) = tl + let x = (max-col-x + 1.5) * spacing + let y = level-y(level-idx) + text-above + + content( + (x, y), + anchor: "north-west", + text(size: 1em * scale-factor, context text(font: phonokit-font.get(), label-text)), + ) + } + + // === Layer 5: Debug references (below labels) === + if refs-visible { + let ref-color = luma(155) + + for (level-idx, row) in tier-grid.enumerate() { + for (col-idx, cell) in row.enumerate() { + let ref-label = debug-ref(level-idx, col-idx, cell) + if ref-label == none { continue } + + let x = cell-x(level-idx, col-idx) + let y = cell-y(level-idx, col-idx) + text-below + + content( + (x, y), + anchor: "south", + text( + size: 0.75em * scale-factor, + fill: ref-color, + font: "Courier New", + ref-label, + ), + ) + } + } + } + + // === Layer 6: Delink cross marks (on top of everything) === + for d in resolved-delinks { + let ((l1, c1), (l2, c2)) = d + + let (p1, p2) = line-endpoints(l1, c1, l2, c2) + let (x1, y1) = p1 + let (x2, y2) = p2 + + let mid-x = (x1 + x2) / 2 + let mid-y = (y1 + y2) / 2 + + let dx = x2 - x1 + let dy = y2 - y1 + let length = calc.sqrt(dx * dx + dy * dy) + + if length == 0 { continue } + + let dir-x = dx / length + let dir-y = dy / length + + let perp-x = -dir-y + let perp-y = dir-x + + let offset = 0.15 + let spacing-offset = 0.06 + + let p1-start = ( + mid-x - offset * perp-x - spacing-offset * dir-x, + mid-y - offset * perp-y - spacing-offset * dir-y, + ) + let p1-end = ( + mid-x + offset * perp-x - spacing-offset * dir-x, + mid-y + offset * perp-y - spacing-offset * dir-y, + ) + + let p2-start = ( + mid-x - offset * perp-x + spacing-offset * dir-x, + mid-y - offset * perp-y + spacing-offset * dir-y, + ) + let p2-end = ( + mid-x + offset * perp-x + spacing-offset * dir-x, + mid-y + offset * perp-y + spacing-offset * dir-y, + ) + + line(p1-start, p1-end, stroke: sw) + line(p2-start, p2-end, stroke: sw) + } + + // === Layer 7: Arrows (rectangular paths above top / below bottom level) === + let arrow-clearance = 0.5 + + for (arrow-idx, a) in resolved-arrows.enumerate() { + let ((l1, c1), (l2, c2)) = a + + let is-top = l1 == 0 and l2 == 0 + let is-bot = l1 == num-levels - 1 and l2 == num-levels - 1 + + let x1 = cell-x(l1, c1) + let x2 = cell-x(l2, c2) + + if is-top { + let y1 = cell-y(l1, c1) + line-top + let y2 = cell-y(l2, c2) + line-top + let y-bar = level-y(0) + line-top + arrow-clearance + + line((x1, y1), (x1, y-bar), stroke: sw) + line((x1, y-bar), (x2, y-bar), stroke: sw) + line((x2, y-bar), (x2, y2), stroke: sw, mark: (end: "stealth"), fill: black) + + if arrow-idx in arrow-delinks { + let mid-x = (x1 + x2) / 2 + let cross-h = 0.15 + let cross-gap = 0.06 + line((mid-x - cross-gap, y-bar - cross-h), (mid-x - cross-gap, y-bar + cross-h), stroke: sw) + line((mid-x + cross-gap, y-bar - cross-h), (mid-x + cross-gap, y-bar + cross-h), stroke: sw) + } + } else if is-bot { + let y1 = cell-y(l1, c1) + line-bot + let y2 = cell-y(l2, c2) + line-bot + let y-bar = level-y(num-levels - 1) + line-bot - arrow-clearance + + line((x1, y1), (x1, y-bar), stroke: sw) + line((x1, y-bar), (x2, y-bar), stroke: sw) + line((x2, y-bar), (x2, y2), stroke: sw, mark: (end: "stealth"), fill: black) + + if arrow-idx in arrow-delinks { + let mid-x = (x1 + x2) / 2 + let cross-h = 0.15 + let cross-gap = 0.06 + line((mid-x - cross-gap, y-bar - cross-h), (mid-x - cross-gap, y-bar + cross-h), stroke: sw) + line((mid-x + cross-gap, y-bar - cross-h), (mid-x + cross-gap, y-bar + cross-h), stroke: sw) + } + } + } + })) +} diff --git a/packages/preview/phonokit/0.5.11/ot.typ b/packages/preview/phonokit/0.5.11/ot.typ new file mode 100644 index 0000000000..f27c769ced --- /dev/null +++ b/packages/preview/phonokit/0.5.11/ot.typ @@ -0,0 +1,1263 @@ +#import "ipa.typ": * +#import "_config.typ": phonokit-font +#import "prosody.typ": foot, foot-mora, mora, syllable, word, word-mora + +#let finger = text(size: 14pt)[☞] +#let viol-sym = text(size: 1.2em)[#sym.ast] + +// Helper: Format constraint names with smallcaps, but not text inside brackets +#let format-constraint(name) = { + // Match pattern: text before bracket, bracket content, text after + let bracket-match = name.match(regex("^([^\[]*)\[([^\]]*)\](.*)$")) + + if bracket-match != none { + // Has brackets: smallcaps before, regular inside brackets, smallcaps after + let before = bracket-match.captures.at(0) + let inside = bracket-match.captures.at(1) + let after = bracket-match.captures.at(2) + + // Compose the parts + [#smallcaps(before)\[#inside\]#if after != "" { smallcaps(after) }] + } else { + // No brackets: all smallcaps + smallcaps(name) + } +} + +// --- Helper: Parse Violation String --- +#let format-viol(v) = { + if v == "" { return [] } + let parts = () + let stars = v.matches("*").len() + let fatal = v.contains("!") + for _ in range(stars) { parts.push(viol-sym) } + if fatal { parts.push(strong("!")) } + parts.join(h(1pt)) +} + +// --- Helper: Parse string with rich formatting --- +// - _{...} or _x: subscript (not IPA-parsed) +// - ^{...} or ^x: superscript (not IPA-parsed) +// - {...}: raw text (not IPA-parsed) +// - \\{ and \\}: literal brace characters +// - \\,: literal comma (since , maps to secondary stress) +// - everything else: IPA-parsed as usual +#let _ends-with-space(s) = { + let cls = s.clusters() + cls.len() > 0 and cls.at(cls.len() - 1) == " " +} + +#let parse-ot-string(s) = { + if type(s) != str { return s } + + let clusters = s.clusters() + let parts = () + let buf = "" + let i = 0 + let len = clusters.len() + + while i < len { + let ch = clusters.at(i) + + if ( + ch == "\\" + and i + 1 < len + and (clusters.at(i + 1) == "{" or clusters.at(i + 1) == "}" or clusters.at(i + 1) == ",") + ) { + let literal = clusters.at(i + 1) + let tighten = buf != "" and not _ends-with-space(buf) + if buf != "" { + parts.push(ipa(buf)) + buf = "" + } + let tighten-after = literal == "{" and i + 2 < len and clusters.at(i + 2) != " " + let lit = context text(font: phonokit-font.get(), literal) + let before-kern = if literal == "}" { -0.3em } else { -0.25em } + let after-kern = if literal == "{" { -0.3em } else { -0.15em } + let lit = if tighten { [#h(before-kern)#lit] } else { lit } + parts.push(if tighten-after { [#lit#h(after-kern)] } else { lit }) + i += 2 + } else if (ch == "_" or ch == "^") and i + 1 < len { + let tighten = buf != "" and not _ends-with-space(buf) + if buf != "" { + parts.push(ipa(buf)) + buf = "" + } + let is-sub = ch == "_" + i += 1 + if clusters.at(i) == "{" { + i += 1 + let group = "" + while i < len and clusters.at(i) != "}" { + group += clusters.at(i) + i += 1 + } + if i < len { i += 1 } + let script = if is-sub { + sub(context text(font: phonokit-font.get(), group)) + } else { + super(context text(font: phonokit-font.get(), group)) + } + if is-sub { + parts.push(if tighten { [#h(-0.25em)#script] } else { script }) + } else { + parts.push(if tighten { [#h(-0.25em)#script] } else { script }) + } + } else { + let c = clusters.at(i) + i += 1 + let script = if is-sub { + sub(context text(font: phonokit-font.get(), c)) + } else { + super(context text(font: phonokit-font.get(), c)) + } + if is-sub { + parts.push(if tighten { [#h(-0.25em)#script] } else { script }) + } else { + parts.push(if tighten { [#h(-0.25em)#script] } else { script }) + } + } + } else if ch == "{" { + let tighten = buf != "" and not _ends-with-space(buf) + if buf != "" { + parts.push(ipa(buf)) + buf = "" + } + i += 1 + let raw = "" + while i < len and clusters.at(i) != "}" { + raw += clusters.at(i) + i += 1 + } + if i < len { i += 1 } + let raw-text = context text(font: phonokit-font.get(), raw) + parts.push(if tighten { [#h(-0.25em)#raw-text] } else { raw-text }) + } else { + buf += ch + i += 1 + } + } + + if buf != "" { parts.push(ipa(buf)) } + if parts.len() == 0 { return [] } + parts.fold([], (acc, part) => [#acc#part]) +} + +// --- Helper: Dispatch prosody function by name --- +#let dispatch-prosody(func-name, arg, ps) = { + if func-name == "syllable" { syllable(arg, scale: ps) } else if func-name == "mora" { mora(arg, scale: ps) } else if ( + func-name == "foot" + ) { foot(arg, scale: ps) } else if func-name == "foot-mora" { foot-mora(arg, scale: ps) } else if ( + func-name == "word" + ) { word(arg, scale: ps) } else if func-name == "word-mora" { word-mora(arg, scale: ps) } else { ipa(arg) } +} + +// --- Helper: Parse candidate string for prosodic function calls --- +#let prosody-pattern = regex("#(syllable|mora|foot-mora|foot|word-mora|word)\\('([^']*)'\\)") + +#let parse-candidate(cand, ps) = { + if type(cand) != str { return cand } + + let all-matches = cand.matches(prosody-pattern) + + if all-matches.len() == 0 { + return parse-ot-string(cand) + } + + let parts = () + let pos = 0 + + for m in all-matches { + if m.start > pos { + let before = cand.slice(pos, m.start).trim() + if before != "" and before != "+" { + parts.push(parse-ot-string(before)) + } + } + + let func-name = m.captures.at(0) + let arg = m.captures.at(1) + parts.push(dispatch-prosody(func-name, arg, ps)) + + pos = m.end + } + + if pos < cand.len() { + let after = cand.slice(pos).trim() + if after != "" and after != "+" { + parts.push(parse-ot-string(after)) + } + } + + parts.join() +} + +#let has-prosody(c) = { + if type(c) == str { c.matches(prosody-pattern).len() > 0 } else { type(c) == content } +} + +// NOTE: --- The Main Function --- +#let tableau( + input: "Input", + candidates: (), + constraints: (), + violations: (), + winner: 0, + dashed-lines: (), + scale: none, + shade: true, + prosody-scale: 0.5, + letters: false, + gloss: none, +) = { + // 1. Validation and Truncation + assert(constraints.len() <= 20, message: "Maximum 20 constraints allowed in tableau") + + // Truncate constraint names to 20 characters + let constraints = constraints.map(c => { + if c.len() > 20 { c.slice(0, 20) } else { c } + }) + + // Scale: use user-provided scale if given, otherwise auto-scale + let scale-factor = if scale != none { + scale + } else if constraints.len() > 6 { + 0.85 + } else { + 1.0 + } + let font-size = scale-factor * 1em + let scaled-finger = text(size: 14pt * scale-factor)[☞] + + // 2. Shading Logic (only if enabled) + let fatal-map = () + if shade { + for (r, row-viols) in violations.enumerate() { + let fatal-col = 999 + for (c, cell) in row-viols.enumerate() { + if cell.contains("!") { + fatal-col = c + break + } + } + fatal-map.push(fatal-col) + } + // Winner shading: shade after the rightmost fatal column among all losers + if winner != none and winner < fatal-map.len() { + let loser-fatals = fatal-map.enumerate().filter(((r, fc)) => r != winner and fc != 999) + if loser-fatals.len() > 0 { + let max-fatal = calc.max(..loser-fatals.map(((_, fc)) => fc)) + fatal-map.at(winner) = max-fatal + } + } + } + + // 3. Prepare Input Content + let gloss-content = if gloss != none { + [ _#gloss.at(0)_ '#gloss.at(1)'] + } else { [] } + let input-content = if type(input) == str { + [#parse-ot-string(input)#gloss-content] + } else { + [#input#gloss-content] + } + + // 4. Grid Definitions + let letter-labels = "abcdefghijklmnopqrstuvwxyz" + let cons-start = 3 // first constraint column index + let row-defs = (1.75em, 2pt) + candidates.map(c => if has-prosody(c) { auto } else { 1.75em }) + + context { + let text-style(it) = text(size: font-size, font: phonokit-font.get(), it) + + // Measure input-content (it spans col 0 and 1) + // Inset for spanned cell: (left: 5pt, right: 10pt) -> 15pt total + let w-input = measure(text-style(input-content)).width + 15pt + + // Measure Col 0 (Prefix/Finger) + // Inset for Col 0: (left: 5pt, right: 0pt) -> 5pt total + let w-col0-max = 0pt + for (i, cand) in candidates.enumerate() { + let finger-content = if i == winner { scaled-finger + " " } else { "" } + let it = if letters { + let letter = letter-labels.at(calc.min(i, 25)) + [#finger-content #letter.] + } else { + [#finger-content] + } + w-col0-max = calc.max(w-col0-max, measure(text-style(it)).width) + } + w-col0-max += 5pt + + // Measure Col 1 (Candidate) + // Inset for Col 1: (left: 8pt, right: 10pt) -> 18pt total + let w-col1-max = 0pt + for (i, cand) in candidates.enumerate() { + let cand-content = parse-candidate(cand, prosody-scale) + w-col1-max = calc.max(w-col1-max, measure(text-style(cand-content)).width) + } + w-col1-max += 18pt + + // Distribution: if input is wider than col0+col1, stretch col0 + let w0 = w-col0-max + let w1 = w-col1-max + if w-input > w0 + w1 { + w0 = w-input - w1 + } + + let col-defs = (w0, w1, 2pt) + constraints.map(_ => auto) + + text-style[#table( + columns: col-defs, + rows: row-defs, + align: (col, row) => { + let v-align = bottom + if row >= 2 { + if col <= 1 { + if has-prosody(candidates.at(row - 2)) { v-align = horizon } + } else { + v-align = horizon + } + } + if col <= 1 { right + v-align } else { center + v-align } + }, + inset: (col, row) => if col == 0 { + (left: 5pt, top: 5pt, bottom: 5pt, right: 0pt) + } else if col == 1 { + (left: 8pt, top: 5pt, bottom: 5pt, right: 10pt) + } else { 5pt }, + + stroke: (col, row) => { + let s = 0.4pt + black + if col == 0 { return (left: s, top: s, bottom: s, right: none) } + if col == 1 { return (left: none, top: s, bottom: s, right: s) } + let is-dashed = if col >= cons-start { dashed-lines.contains(col - cons-start) } else { false } + ( + left: s, + top: s, + bottom: s, + right: if is-dashed { (thickness: 0.4pt, dash: "dashed") } else { s }, + ) + }, + + fill: (col, row) => { + if not shade or row < 2 or col < cons-start { return none } + let cand-idx = row - 2 + let cons-idx = col - cons-start + if cand-idx < fatal-map.len() { + let fatal-col = fatal-map.at(cand-idx) + if cons-idx > fatal-col { + let has-solid-line = false + for c in range(fatal-col, cons-idx) { + if not dashed-lines.contains(c) { + has-solid-line = true + break + } + } + if has-solid-line { return luma(230) } + } + } + return none + }, + + // --- Content --- + table.cell(colspan: 2, inset: (left: 5pt, right: 10pt), align: right + bottom, input-content), + [], + ..constraints.map(c => format-constraint(c)), + + // Gap Row + ..range(col-defs.len()).map(_ => []), + + // Candidates + ..candidates + .enumerate() + .map(((i, cand)) => { + let cells = () + let cand-content = parse-candidate(cand, prosody-scale) + + let finger-content = if i == winner { scaled-finger + " " } else { "" } + if letters { + let letter = letter-labels.at(calc.min(i, 25)) + cells.push([#finger-content #letter.]) + } else { + cells.push([#finger-content]) + } + cells.push([#cand-content]) + cells.push([]) + + let row-viols = if i < violations.len() { violations.at(i) } else { () } + for j in range(constraints.len()) { + if j < row-viols.len() { + cells.push(format-viol(row-viols.at(j))) + } else { + cells.push([]) + } + } + return cells + }) + .flatten() + )] + } +} + +// NOTE: --- HG TABLEAU FUNCTION --- +#let hg( + input: "Input", + candidates: (), + constraints: (), + weights: (), + violations: (), + scale: none, + letters: false, +) = { + // 1. Validation and Truncation + assert(constraints.len() <= 20, message: "Maximum 20 constraints allowed") + let letter-labels = "abcdefghijklmnopqrstuvwxyz" + + // Truncate constraint names to 15 characters + let constraints = constraints.map(c => { + if c.len() > 15 { c.slice(0, 15) } else { c } + }) + + // Scale: use user-provided scale if given, otherwise auto-scale + let font-size = if scale != none { + scale * 1em + } else if constraints.len() > 6 { + 0.85em + } else { + 0.95em + } + + // 2. CALCULATIONS + let h-scores = () + + for row-viols in violations { + let h = 0.0 + for (i, v) in row-viols.enumerate() { + if i < weights.len() { + h += float(v) * float(weights.at(i)) + } + } + h-scores.push(h) + } + + // 3. GRID DEFINITIONS (simpler than maxent - only h(y) column) + let row-defs = (auto, 1.75em, 2pt) + candidates.map(c => if has-prosody(c) { auto } else { 1.75em }) + + context { + let text-style(it) = text(size: font-size, font: phonokit-font.get(), it) + let input-content = if type(input) == str { parse-ot-string(input) } else { input } + + // Measure input-content (it spans col 0 and 1) + let w-input = measure(text-style(input-content)).width + 15pt + + // Measure Col 0 (Prefix) + let w-col0-max = 0pt + for (i, cand) in candidates.enumerate() { + let it = if letters { + let letter = letter-labels.at(calc.min(i, 25)) + [#letter.] + } else { + [] + } + w-col0-max = calc.max(w-col0-max, measure(text-style(it)).width) + } + w-col0-max += 5pt + + // Measure Col 1 (Candidate) + let w-col1-max = 0pt + for (i, cand) in candidates.enumerate() { + let cand-content = parse-candidate(cand, 0.5) + w-col1-max = calc.max(w-col1-max, measure(text-style(cand-content)).width) + } + w-col1-max += 18pt + + // Distribution + let w0 = w-col0-max + let w1 = w-col1-max + if w-input > w0 + w1 { + w0 = w-input - w1 + } + + let col-defs = (w0, w1, 2pt) + constraints.map(_ => auto) + (2pt, auto) + + text-style[#table( + columns: col-defs, + rows: row-defs, + align: (col, row) => { + let v-align = bottom + if row >= 3 { + if col <= 1 { + if has-prosody(candidates.at(row - 3)) { v-align = horizon } + } else { + v-align = horizon + } + } + if col <= 1 { right + v-align } else { center + v-align } + }, + inset: (col, row) => if col == 0 { + (left: 5pt, top: 5pt, bottom: 5pt, right: 0pt) + } else if col == 1 { + (left: 8pt, top: 5pt, bottom: 5pt, right: 10pt) + } else { 5pt }, + + // --- STROKE LOGIC --- + stroke: (col, row) => { + if row == 0 { return none } + let s = 0.4pt + black + if col == 0 { return (left: s, top: s, bottom: s, right: none) } + if col == 1 { return (left: none, top: s, bottom: s, right: s) } + (left: s, top: s, bottom: s, right: s) + }, + + // --- ROW 0: WEIGHTS + [], [], [], + ..weights.map(w => text(size: 0.9em)[$w=#w$]), + // Fill remaining columns: gap (2pt) + h(y) column + [], [], + + // --- ROW 1: HEADERS --- + table.cell(colspan: 2, inset: (left: 5pt, right: 10pt), align: right + bottom, input-content), + [], + ..constraints.map(c => format-constraint(c)), + [], + [$h_i$], + + // --- ROW 2: GAP --- + ..range(col-defs.len()).map(_ => []), + + // --- ROWS 3+: CANDIDATES --- + ..candidates + .enumerate() + .map(((i, cand)) => { + let cells = () + let cand-content = parse-candidate(cand, 0.5) + + if letters { + let letter = letter-labels.at(calc.min(i, 25)) + cells.push([#letter.]) + } else { + cells.push([]) + } + cells.push([#cand-content]) + cells.push([]) + + // Violations + let row-viols = if i < violations.len() { violations.at(i) } else { () } + for j in range(constraints.len()) { + if j < row-viols.len() { cells.push(text(size: 0.85em)[#str(row-viols.at(j))]) } else { cells.push([]) } + } + + cells.push([]) + + // Only h(y) column - no MaxEnt probabilities + if i < h-scores.len() { + cells.push(text(size: 0.85em)[#str(calc.round(h-scores.at(i), digits: 2))]) + } else { + cells.push("-") + } + + return cells + }) + .flatten() + )] + } +} + +// NOTE: --- NHG DEMO TABLEAU (Pedagogical - shows symbolic noise) --- +#let nhg-demo( + input: "Input", + candidates: (), + constraints: (), + weights: (), + violations: (), + probabilities: none, // optional: if provided, will display P(y) values + scale: none, + letters: false, +) = { + // 1. Validation and Truncation + assert(constraints.len() <= 20, message: "Maximum 20 constraints allowed") + let letter-labels = "abcdefghijklmnopqrstuvwxyz" + + // Truncate constraint names to 15 characters + let constraints = constraints.map(c => { + if c.len() > 15 { c.slice(0, 15) } else { c } + }) + + // Scale: use user-provided scale if given, otherwise auto-scale + let font-size = if scale != none { + scale * 1em + } else if constraints.len() > 6 { + 0.85em + } else { + 0.95em + } + + // 2. CALCULATIONS + let h-scores = () + let epsilon-formulas = () + + for row-viols in violations { + // Calculate deterministic harmony h(y) + let h = 0.0 + for (i, v) in row-viols.enumerate() { + if i < weights.len() { + h += float(v) * float(weights.at(i)) + } + } + h-scores.push(h) + + // Build epsilon formula: ε(y) = Σ(n_i × v_i) + let epsilon-parts = () + for (i, v) in row-viols.enumerate() { + if i < constraints.len() and v != 0 { + let v-val = float(v) + let abs-v = calc.abs(v-val) + let sign = if v-val < 0 { "-" } else { "" } + let coef = if abs-v == 1 { "" } else { str(int(abs-v)) } + epsilon-parts.push(sign + coef + "n_" + str(i + 1)) + } + } + + // Join epsilon parts and create single math expression + let epsilon-formula = if epsilon-parts.len() > 0 { + let formula-str = epsilon-parts.join(" ") + eval("$" + formula-str + "$") + } else { + $0$ + } + epsilon-formulas.push(epsilon-formula) + } + + // 3. GRID DEFINITIONS (h, ε, P columns) + let row-defs = (auto, 1.75em, 2pt) + candidates.map(c => if has-prosody(c) { auto } else { 1.75em }) + + context { + let text-style(it) = text(size: font-size, font: phonokit-font.get(), it) + let input-content = if type(input) == str { parse-ot-string(input) } else { input } + + // Measure input-content (it spans col 0 and 1) + let w-input = measure(text-style(input-content)).width + 15pt + + // Measure Col 0 (Prefix) + let w-col0-max = 0pt + for (i, cand) in candidates.enumerate() { + let it = if letters { + let letter = letter-labels.at(calc.min(i, 25)) + [#letter.] + } else { + [] + } + w-col0-max = calc.max(w-col0-max, measure(text-style(it)).width) + } + w-col0-max += 5pt + + // Measure Col 1 (Candidate) + let w-col1-max = 0pt + for (i, cand) in candidates.enumerate() { + let cand-content = parse-candidate(cand, 0.5) + w-col1-max = calc.max(w-col1-max, measure(text-style(cand-content)).width) + } + w-col1-max += 18pt + + // Distribution + let w0 = w-col0-max + let w1 = w-col1-max + if w-input > w0 + w1 { + w0 = w-input - w1 + } + + let col-defs = (w0, w1, 2pt) + constraints.map(_ => auto) + (2pt, auto, auto) + if probabilities != none { + col-defs.push(auto) // Add P(y) column + } + + text-style[#table( + columns: col-defs, + rows: row-defs, + align: (col, row) => { + let v-align = bottom + if row >= 3 { + if col <= 1 { + if has-prosody(candidates.at(row - 3)) { v-align = horizon } + } else { + v-align = horizon + } + } + if col <= 1 { right + v-align } else { center + v-align } + }, + inset: (col, row) => if col == 0 { + (left: 5pt, top: 5pt, bottom: 5pt, right: 0pt) + } else if col == 1 { + (left: 8pt, top: 5pt, bottom: 5pt, right: 10pt) + } else { 5pt }, + + // --- STROKE LOGIC --- + stroke: (col, row) => { + if row == 0 { return none } + let s = 0.4pt + black + if col == 0 { return (left: s, top: s, bottom: s, right: none) } + if col == 1 { return (left: none, top: s, bottom: s, right: s) } + (left: s, top: s, bottom: s, right: s) + }, + + // --- ROW 0: WEIGHTS + [], [], [], + ..weights.map(w => text(size: 0.9em)[$w=#w$]), + // Fill remaining columns: gap + h + ε + (optional P) + ..range(if probabilities != none { 4 } else { 3 }).map(_ => []), + + // --- ROW 1: HEADERS --- + table.cell(colspan: 2, inset: (left: 5pt, right: 10pt), align: right + bottom, input-content), + [], + ..constraints.map(c => format-constraint(c)), + [], + [$h_i$], + [$epsilon_i$], + ..(if probabilities != none { ([$P_i$],) } else { () }), + + // --- ROW 2: GAP --- + ..range(col-defs.len()).map(_ => []), + + // --- ROWS 3+: CANDIDATES --- + ..candidates + .enumerate() + .map(((i, cand)) => { + let cells = () + let cand-content = parse-candidate(cand, 0.5) + + if letters { + let letter = letter-labels.at(calc.min(i, 25)) + cells.push([#letter.]) + } else { + cells.push([]) + } + cells.push([#cand-content]) + cells.push([]) + + // Violations + let row-viols = if i < violations.len() { violations.at(i) } else { () } + for j in range(constraints.len()) { + if j < row-viols.len() { + cells.push(text(size: 0.85em)[#str(row-viols.at(j))]) + } else { + cells.push([]) + } + } + + cells.push([]) + + // h(y) column + if i < h-scores.len() { + cells.push(text(size: 0.85em)[#str(calc.round(h-scores.at(i), digits: 2))]) + } else { + cells.push("-") + } + + // ε(y) column (formula) + if i < epsilon-formulas.len() { + cells.push(text(size: 0.85em)[#epsilon-formulas.at(i)]) + } else { + cells.push("-") + } + + // P(y) column (if provided) + if probabilities != none and i < probabilities.len() { + cells.push(text(size: 0.85em)[#str(calc.round(probabilities.at(i), digits: 3))]) + } else if probabilities != none { + cells.push("-") + } + + return cells + }) + .flatten() + )] + } +} + +// NOTE: --- NHG TABLEAU (Smart - samples noise and calculates probabilities) --- +#let nhg( + input: "Input", + candidates: (), + constraints: (), + weights: (), + violations: (), + num-simulations: 1000, + seed: none, + show-epsilon: true, + scale: none, + letters: false, +) = { + // 1. Validation and Truncation + assert(constraints.len() <= 20, message: "Maximum 20 constraints allowed") + let letter-labels = "abcdefghijklmnopqrstuvwxyz" + + // Truncate constraint names to 15 characters + let constraints = constraints.map(c => { + if c.len() > 15 { c.slice(0, 15) } else { c } + }) + + // Scale: use user-provided scale if given, otherwise auto-scale + let font-size = if scale != none { + scale * 1em + } else if constraints.len() > 6 { + 0.85em + } else { + 0.95em + } + + // 2. Generate all random samples upfront using LCG + let initial-seed = if seed != none { seed } else { 12345 } + let total-samples = (num-simulations + 1) * constraints.len() + + let normal-samples = () + let state = initial-seed + + for i in range(total-samples) { + // Generate two uniform random numbers for Box-Muller + state = calc.rem(state * 1103515245 + 12345, 2147483648) + let u1 = state / 2147483648.0 + state = calc.rem(state * 1103515245 + 12345, 2147483648) + let u2 = state / 2147483648.0 + + // Box-Muller transform + if u1 < 0.00001 { u1 = 0.00001 } + let sample = calc.sqrt(-2.0 * calc.ln(u1)) * calc.cos(2.0 * calc.pi * u2) + normal-samples.push(sample) + } + + // 3. Calculate deterministic harmonies + let h-scores = () + for row-viols in violations { + let h = 0.0 + for (i, v) in row-viols.enumerate() { + if i < weights.len() { + h += float(v) * float(weights.at(i)) + } + } + h-scores.push(h) + } + + // 4. Monte Carlo simulation + let win-counts = candidates.map(_ => 0) + let sample-idx = 0 + + for sim in range(num-simulations) { + // Get noise for this simulation + let noise = () + for c in range(constraints.len()) { + noise.push(normal-samples.at(sample-idx)) + sample-idx = sample-idx + 1 + } + + // Calculate noisy harmonies + let noisy-harmonies = () + for (cand-idx, row-viols) in violations.enumerate() { + let h = h-scores.at(cand-idx) + let epsilon = 0.0 + for (i, v) in row-viols.enumerate() { + if i < noise.len() { + epsilon += noise.at(i) * float(v) + } + } + noisy-harmonies.push(h + epsilon) + } + + // Find winner + let max-harmony = calc.max(..noisy-harmonies) + let winner-idx = noisy-harmonies.position(h => h == max-harmony) + if winner-idx != none { + win-counts.at(winner-idx) = win-counts.at(winner-idx) + 1 + } + } + + // Calculate probabilities + let probabilities = win-counts.map(count => float(count) / float(num-simulations)) + + // 5. Get epsilon values for display (one more set of samples) + let display-noise = () + for c in range(constraints.len()) { + display-noise.push(normal-samples.at(sample-idx)) + sample-idx = sample-idx + 1 + } + + let epsilon-values = () + for row-viols in violations { + let epsilon = 0.0 + for (i, v) in row-viols.enumerate() { + if i < display-noise.len() { + epsilon += display-noise.at(i) * float(v) + } + } + epsilon-values.push(epsilon) + } + + // 6. GRID DEFINITIONS + let row-defs = (auto, 1.75em, 2pt) + candidates.map(c => if has-prosody(c) { auto } else { 1.75em }) + + context { + let text-style(it) = text(size: font-size, font: phonokit-font.get(), it) + let input-content = if type(input) == str { parse-ot-string(input) } else { input } + + // Measure input-content (it spans col 0 and 1) + let w-input = measure(text-style(input-content)).width + 15pt + + // Measure Col 0 (Prefix) + let w-col0-max = 0pt + for (i, cand) in candidates.enumerate() { + let it = if letters { + let letter = letter-labels.at(calc.min(i, 25)) + [#letter.] + } else { + [] + } + w-col0-max = calc.max(w-col0-max, measure(text-style(it)).width) + } + w-col0-max += 5pt + + // Measure Col 1 (Candidate) + let w-col1-max = 0pt + for (i, cand) in candidates.enumerate() { + let cand-content = parse-candidate(cand, 0.5) + w-col1-max = calc.max(w-col1-max, measure(text-style(cand-content)).width) + } + w-col1-max += 18pt + + // Distribution + let w0 = w-col0-max + let w1 = w-col1-max + if w-input > w0 + w1 { + w0 = w-input - w1 + } + + let col-defs = (w0, w1, 2pt) + constraints.map(_ => auto) + (2pt, auto) + if show-epsilon { + col-defs.push(auto) // epsilon column + } + col-defs.push(auto) // P(y) column + + text-style[#table( + columns: col-defs, + rows: row-defs, + align: (col, row) => { + let v-align = bottom + if row >= 3 { + if col <= 1 { + if has-prosody(candidates.at(row - 3)) { v-align = horizon } + } else { + v-align = horizon + } + } + if col <= 1 { right + v-align } else { center + v-align } + }, + inset: (col, row) => if col == 0 { + (left: 5pt, top: 5pt, bottom: 5pt, right: 0pt) + } else if col == 1 { + (left: 8pt, top: 5pt, bottom: 5pt, right: 10pt) + } else { 5pt }, + + // --- STROKE LOGIC --- + stroke: (col, row) => { + if row == 0 { return none } + let s = 0.4pt + black + if col == 0 { return (left: s, top: s, bottom: s, right: none) } + if col == 1 { return (left: none, top: s, bottom: s, right: s) } + (left: s, top: s, bottom: s, right: s) + }, + + // --- ROW 0: WEIGHTS + [], [], [], + ..weights.map(w => text(size: 0.9em)[$w=#w$]), + // Fill remaining columns: gap + h + optional ε + P + ..range(if show-epsilon { 4 } else { 3 }).map(_ => []), + + // --- ROW 1: HEADERS --- + table.cell(colspan: 2, inset: (left: 5pt, right: 10pt), align: right + bottom, input-content), + [], + ..constraints.map(c => format-constraint(c)), + [], + [$h_i$], + ..(if show-epsilon { ([$epsilon_i$],) } else { () }), + [$P_i$], + + // --- ROW 2: GAP --- + ..range(col-defs.len()).map(_ => []), + + // --- ROWS 3+: CANDIDATES --- + ..candidates + .enumerate() + .map(((i, cand)) => { + let cells = () + let cand-content = parse-candidate(cand, 0.5) + + if letters { + let letter = letter-labels.at(calc.min(i, 25)) + cells.push([#letter.]) + } else { + cells.push([]) + } + cells.push([#cand-content]) + cells.push([]) + + // Violations + let row-viols = if i < violations.len() { violations.at(i) } else { () } + for j in range(constraints.len()) { + if j < row-viols.len() { + cells.push(text(size: 0.85em)[#str(row-viols.at(j))]) + } else { + cells.push([]) + } + } + + cells.push([]) + + // h(y) column + if i < h-scores.len() { + cells.push(text(size: 0.85em)[#str(calc.round(h-scores.at(i), digits: 2))]) + } else { + cells.push("-") + } + + // ε(y) column (sampled value) - only if show-epsilon + if show-epsilon { + if i < epsilon-values.len() { + cells.push(text(size: 0.85em)[#str(calc.round(epsilon-values.at(i), digits: 2))]) + } else { + cells.push("-") + } + } + + // P(y) column (estimated from simulations) + if i < probabilities.len() { + cells.push(text(size: 0.85em)[#str(calc.round(probabilities.at(i), digits: 3))]) + } else { + cells.push("-") + } + + return cells + }) + .flatten() + )] + } +} + +// NOTE: --- MAXENT TABLEAU FUNCTION --- +#let maxent( + input: "Input", + candidates: (), + constraints: (), + weights: (), + violations: (), + visualize: true, + sort: false, + scale: none, + letters: false, +) = { + // 1. Validation and Truncation + assert(constraints.len() <= 20, message: "Maximum 20 constraints allowed in maxent") + let letter-labels = "abcdefghijklmnopqrstuvwxyz" + + // Truncate constraint names to 15 characters + let constraints = constraints.map(c => { + if c.len() > 15 { c.slice(0, 15) } else { c } + }) + + // Scale: use user-provided scale if given, otherwise auto-scale + let font-size = if scale != none { + scale * 1em + } else if constraints.len() > 6 { + 0.85em + } else { + 0.95em + } + + // 2. CALCULATIONS + let h-scores = () + let p-star-scores = () + let total-p-star = 0.0 + + for row-viols in violations { + let h = 0.0 + for (i, v) in row-viols.enumerate() { + if i < weights.len() { + h += float(v) * float(weights.at(i)) + } + } + h-scores.push(h) + let p-star = calc.exp(-h) + p-star-scores.push(p-star) + total-p-star += p-star + } + + // Safety check for empty violations/division by zero + let p-scores = if total-p-star > 0 { + p-star-scores.map(x => x / total-p-star) + } else { + candidates.map(_ => 0.0) + } + + // 3. SORT BY PROBABILITY (if enabled) + let order = if sort { + range(candidates.len()).sorted(key: i => -p-scores.at(i)) + } else { + range(candidates.len()) + } + let candidates = order.map(i => candidates.at(i)) + let violations = order.map(i => violations.at(i)) + let h-scores = order.map(i => h-scores.at(i)) + let p-star-scores = order.map(i => p-star-scores.at(i)) + let p-scores = order.map(i => p-scores.at(i)) + + // 4. GRID DEFINITIONS + let bar-col-width = 3cm + let row-defs = (auto, 1.75em, 2pt) + candidates.map(c => if has-prosody(c) { auto } else { 1.75em }) + + context { + let text-style(it) = text(size: font-size, font: phonokit-font.get(), it) + let input-content = if type(input) == str { parse-ot-string(input) } else { input } + + // Measure input-content (it spans col 0 and 1) + let w-input = measure(text-style(input-content)).width + 15pt + + // Measure Col 0 (Prefix) + let w-col0-max = 0pt + for (i, cand) in candidates.enumerate() { + let it = if letters { + let letter = letter-labels.at(calc.min(i, 25)) + [#letter.] + } else { + [] + } + w-col0-max = calc.max(w-col0-max, measure(text-style(it)).width) + } + w-col0-max += 5pt + + // Measure Col 1 (Candidate) + let w-col1-max = 0pt + for (i, cand) in candidates.enumerate() { + let cand-content = parse-candidate(cand, 0.5) + w-col1-max = calc.max(w-col1-max, measure(text-style(cand-content)).width) + } + w-col1-max += 18pt + + // Distribution + let w0 = w-col0-max + let w1 = w-col1-max + if w-input > w0 + w1 { + w0 = w-input - w1 + } + + let col-defs = (w0, w1, 2pt) + constraints.map(_ => auto) + (2pt, auto, auto, auto) + if visualize { + col-defs.push(bar-col-width) // The Floating Column + } + let last-col-idx = col-defs.len() - 1 + + let tbl = text-style[#table( + columns: col-defs, + rows: row-defs, + align: (col, row) => { + let v-align = bottom + if row >= 3 { + if col <= 1 { + if has-prosody(candidates.at(row - 3)) { v-align = horizon } + } else { + v-align = horizon + } + } + if col <= 1 { right + v-align } else { center + v-align } + }, + inset: (col, row) => if col == 0 { + (left: 5pt, top: 5pt, bottom: 5pt, right: 0pt) + } else if col == 1 { + (left: 8pt, top: 5pt, bottom: 5pt, right: 10pt) + } else { 5pt }, + + // --- STROKE LOGIC --- + stroke: (col, row) => { + if row == 0 { return none } + if visualize and col == last-col-idx { return none } + let s = 0.4pt + black + if col == 0 { return (left: s, top: s, bottom: s, right: none) } + if col == 1 { return (left: none, top: s, bottom: s, right: s) } + (left: s, top: s, bottom: s, right: s) + }, + + // --- ROW 0: WEIGHTS + [], [], [], + ..weights.map(w => text(size: 0.9em)[$w=#w$]), + // Fill remaining columns with empty cells + ..range(if visualize { 5 } else { 4 }).map(_ => []), + + // --- ROW 1: HEADERS --- + table.cell(colspan: 2, inset: (left: 5pt, right: 10pt), align: right + bottom, input-content), + [], + ..constraints.map(c => format-constraint(c)), + [], + [$h_i$], [$e^(-h_i)$], [$P_i$], + // Add empty floating header if visualizing + ..(if visualize { ([],) } else { () }), + + // --- ROW 2: GAP --- + ..range(col-defs.len()).map(_ => []), + + // --- ROWS 3+: CANDIDATES (letters assigned AFTER sort) + ..candidates + .enumerate() + .map(((i, cand)) => { + let cells = () + let cand-content = parse-candidate(cand, 0.5) + + // Letters are assigned after sorting, so i reflects the sorted order + if letters { + let letter = letter-labels.at(calc.min(i, 25)) + cells.push([#letter.]) + } else { + cells.push([]) + } + cells.push([#cand-content]) + cells.push([]) + + // Violations + let row-viols = if i < violations.len() { violations.at(i) } else { () } + for j in range(constraints.len()) { + if j < row-viols.len() { cells.push(text(size: 0.85em)[#str(row-viols.at(j))]) } else { cells.push([]) } + } + + cells.push([]) + if i < h-scores.len() { + cells.push(text(size: 0.85em)[#str(calc.round(h-scores.at(i), digits: 2))]) + cells.push(text(size: 0.85em)[#str(calc.round(p-star-scores.at(i), digits: 3))]) + let p-val = p-scores.at(i) + cells.push(text(size: 0.85em)[#str(calc.round(p-val, digits: 3))]) + + // --- FLOATING VISUAL BAR --- + if visualize { + cells.push(align(left + horizon)[ + #box(width: 50%, height: 0.5em, stroke: 0.5pt + luma(100))[ + #rect( + width: p-val * 100%, + height: 100%, + fill: luma(100), + stroke: 0.5pt + luma(100), + ) + ] + ]) + } + } else { + // Fallback + cells.push("-") + cells.push("-") + cells.push("-") + if visualize { cells.push([]) } + } + + return cells + }) + .flatten() + )] + + if visualize { pad(right: -bar-col-width, tbl) } else { tbl } + } +} +} diff --git a/packages/preview/phonokit/0.5.11/phonetics.typ b/packages/preview/phonokit/0.5.11/phonetics.typ new file mode 100644 index 0000000000..292829577e --- /dev/null +++ b/packages/preview/phonokit/0.5.11/phonetics.typ @@ -0,0 +1,1017 @@ +#import "@preview/cetz:0.5.2" +#import "@preview/lilaq:0.6.0" as lq +#import "ipa.typ": ipa-to-unicode +#import "_config.typ": phonokit-font +#import "vowels.typ": language-vowels +#import "ui-lang.typ": resolve-ui-lang, ui-lang-error + +#let formants-default-vowel-size = 18pt + +#let phonetic-vowel-presets = ( + "i": (f1: 280, f2: 2290), + "y": (f1: 310, f2: 1990), + "ɨ": (f1: 300, f2: 1750), + "ʉ": (f1: 320, f2: 1400), + "ɯ": (f1: 300, f2: 1100), + "ɪ": (f1: 390, f2: 1990), + "ʏ": (f1: 400, f2: 1750), + "e": (f1: 390, f2: 2200), + "ø": (f1: 390, f2: 1800), + "ɘ": (f1: 420, f2: 1650), + "ɵ": (f1: 420, f2: 1400), + "ɤ": (f1: 420, f2: 1200), + "ɛ": (f1: 530, f2: 1840), + "œ": (f1: 540, f2: 1600), + "ɜ": (f1: 560, f2: 1500), + "ɞ": (f1: 560, f2: 1300), + "æ": (f1: 660, f2: 1720), + "ɐ": (f1: 650, f2: 1450), + "a": (f1: 730, f2: 1500), + "ɶ": (f1: 780, f2: 1450), + "ɑ": (f1: 730, f2: 1090), + "ɒ": (f1: 700, f2: 900), + "ə": (f1: 500, f2: 1500), + "ʌ": (f1: 610, f2: 1300), + "ɔ": (f1: 570, f2: 840), + "o": (f1: 430, f2: 930), + "ʊ": (f1: 440, f2: 1020), + "u": (f1: 300, f2: 870), +) + +// Sourced English preset adapted from the male values in Hillenbrand et al. (1995), +// as provided by the user from a secondary table reproducing those averages. +// For legibility in a teaching plot, a few values are slightly regularized so +// categories do not collapse visually. This is used only when the input preset +// is "english". Other presets remain schematic/illustrative for teaching. +#let english-hillenbrand-male = ( + "i": (f1: 342, f2: 2322), + "ɪ": (f1: 427, f2: 2034), + "e": (f1: 476, f2: 2089), + "ɛ": (f1: 580, f2: 1799), + "æ": (f1: 588, f2: 1952), + "a": (f1: 768, f2: 1425), + "ɑ": (f1: 768, f2: 1225), + "ɔ": (f1: 652, f2: 997), + "o": (f1: 497, f2: 910), + "u": (f1: 378, f2: 997), + "ʊ": (f1: 469, f2: 1122), + "ʌ": (f1: 623, f2: 1200), + "ə": (f1: 474, f2: 1379), + "ɚ": (f1: 474, f2: 1379), +) + +#let _unique(items) = { + let seen = () + for item in items { + if item not in seen { + seen.push(item) + } + } + seen +} + +#let _index-of(items, target) = { + for (idx, item) in items.enumerate() { + if item == target { + return idx + } + } + none +} + +#let _normalize-vowel-string(vowels) = { + let cleaned = vowels + if cleaned in language-vowels { + return language-vowels.at(cleaned).clusters() + } + let normalized = ipa-to-unicode(cleaned) + .replace("/", " ") + .replace("[", " ") + .replace("]", " ") + .replace(",", " ") + .replace(";", " ") + .replace("|", " ") + + let split-tokens = normalized.split(" ").filter(token => token != "") + if split-tokens.len() > 0 { + let expanded = () + for token in split-tokens { + if token in phonetic-vowel-presets { + expanded.push(token) + } else { + let pieces = token.clusters() + if pieces.all(piece => piece in phonetic-vowel-presets) { + for piece in pieces { + expanded.push(piece) + } + } else { + expanded.push(token) + } + } + } + expanded + } else { + normalized.clusters().filter(token => token in phonetic-vowel-presets) + } +} + +#let _next-rand(state) = calc.rem(state * 48271 + 1, 2147483647) + +#let _rand-unit(state) = { + let next = _next-rand(state) + (next, calc.abs(float(next)) / 2147483646.0) +} + +#let _rand-symmetric(state, spread) = { + let state-0 = state + let acc = 0.0 + for _ in range(6) { + let sample = _rand-unit(state-0) + state-0 = sample.at(0) + acc += sample.at(1) + } + (state-0, (acc - 3.0) * spread) +} + +#let _token-cloud(vowels, preset-map, n, sd, sd2, seed) = { + let tokens = () + let state = calc.max(seed, 1) + for (index, vowel) in vowels.enumerate() { + let mean = preset-map.at(vowel) + for token-index in range(n) { + let jitter-f1 = _rand-symmetric(state + index * 101 + token-index * 17, sd) + let jitter-f2 = _rand-symmetric(jitter-f1.at(0) + 29, sd2) + state = jitter-f2.at(0) + tokens.push(( + vowel: vowel, + f1: mean.f1 + jitter-f1.at(1), + f2: mean.f2 + jitter-f2.at(1), + )) + } + } + tokens +} + +#let _centroid-pairs(vowels, preset-map) = { + vowels.map(vowel => { + let mean = preset-map.at(vowel) + ( + vowel: vowel, + f1: mean.f1, + f2: mean.f2, + ) + }) +} + +#let _mean(values) = values.sum() / values.len() + +#let _sd(values) = { + if values.len() <= 1 { return 0.0 } + let mean = _mean(values) + let variance = values.map(value => calc.pow(value - mean, 2)).sum() / (values.len() - 1) + calc.sqrt(variance) +} + +#let _csv-tokens(source) = { + if type(source) != array { + return (error: [*Error:* `source` must be tabular data from `csv(..., row-type: dictionary)`], tokens: ()) + } + if source.len() == 0 { + return (error: [*Error:* `source` is empty], tokens: ()) + } + + let first = source.at(0) + let tokens = () + if type(first) == dictionary { + if not ("vowel" in first and "f1" in first and "f2" in first) { + return (error: [*Error:* CSV source must contain columns `"vowel"`, `"f1"`, and `"f2"`], tokens: ()) + } + for row in source { + if type(row) != dictionary or not ("vowel" in row and "f1" in row and "f2" in row) { + return (error: [*Error:* Every CSV row must contain `"vowel"`, `"f1"`, and `"f2"`], tokens: ()) + } + let vowel = ipa-to-unicode(str(row.at("vowel"))) + let f1 = float(row.at("f1")) + let f2 = float(row.at("f2")) + tokens.push((vowel: vowel, f1: f1, f2: f2)) + } + } else if type(first) == array { + if first.len() < 3 { + return (error: [*Error:* CSV source must contain columns `"vowel"`, `"f1"`, and `"f2"`], tokens: ()) + } + let headers = first.map(value => str(value)) + let vowel-idx = _index-of(headers, "vowel") + let f1-idx = _index-of(headers, "f1") + let f2-idx = _index-of(headers, "f2") + if vowel-idx == none or f1-idx == none or f2-idx == none { + return (error: [*Error:* CSV source must contain columns `"vowel"`, `"f1"`, and `"f2"`], tokens: ()) + } + for row in source.slice(1) { + if type(row) != array or calc.max(vowel-idx, f1-idx, f2-idx) >= row.len() { + return (error: [*Error:* Every CSV row must contain values for `"vowel"`, `"f1"`, and `"f2"`], tokens: ()) + } + let vowel = ipa-to-unicode(str(row.at(vowel-idx))) + let f1 = float(row.at(f1-idx)) + let f2 = float(row.at(f2-idx)) + tokens.push((vowel: vowel, f1: f1, f2: f2)) + } + } else { + return (error: [*Error:* `source` must be tabular data from `csv(...)`], tokens: ()) + } + (error: none, tokens: tokens) +} + +#let _centroids-from-tokens(tokens) = { + let vowels = _unique(tokens.map(token => token.vowel)) + vowels.map(vowel => { + let subset = tokens.filter(token => token.vowel == vowel) + ( + vowel: vowel, + f1: _mean(subset.map(token => token.f1)), + f2: _mean(subset.map(token => token.f2)), + sd-f1: _sd(subset.map(token => token.f1)), + sd-f2: _sd(subset.map(token => token.f2)), + ) + }) +} + +#let _warn(body) = text(fill: red.darken(20%), weight: "bold")[⚠ #body] + +#let _nice-tick-step(span, target: 6) = { + let rough = span / target + if rough <= 50 { 50 } else if rough <= 100 { 100 } else if rough <= 200 { 200 } else if rough <= 250 { 250 } else if ( + rough <= 500 + ) { 500 } else { 1000 } +} + +#let _make-ticks(minimum, maximum, step) = { + let start = int(calc.ceil(minimum / step) * step) + let stop = int(calc.floor(maximum / step) * step) + let ticks = () + let current = start + while current <= stop { + ticks.push(current) + current += step + } + ticks +} + +/// Create an illustrative F1/F2 vowel cloud for teaching. +/// +/// This function generates synthetic vowel tokens around built-in F1/F2 means +/// and displays them on an inverted F1/F2 diagram in the style common in +/// phonetics teaching. +/// +/// Data policy: +/// - If `vowels` is `"english"`, the means are based on the male averages in +/// Hillenbrand et al. (1995), lightly regularized for pedagogical clarity. +/// - Other vowel means in this module are schematic/illustrative defaults for +/// teaching and are not tied to a single empirical source. +/// +/// Arguments: +/// - vowels (string): Tipa-style/IPA vowel string or a built-in language name +/// such as `"english"`. +/// - source (array, optional): Tabular data from `csv(...)` with required +/// columns `vowel`, `f1`, and `f2`. Extra columns are ignored. +/// - sd (float): Standard deviation used for F1 jitter in Hz. +/// - sd2 (float, optional): Standard deviation for F2 jitter in Hz; defaults to `sd`. +/// - n (int): Number of synthetic tokens per vowel (default: 10). +/// - seed (int): Seed for deterministic token placement (default: 1). +/// - labels (bool): Show vowel labels at preset means (default: true). +/// - points (bool): Show synthetic tokens (default: true). +/// - centers (bool): Show explicit `+` mean markers (default: false). +/// - ellipse (bool): Show 1-SD ellipses centered on the vowel means (default: false). +/// - ellipse-stroke (stroke or auto): Stroke used for SD ellipses +/// (default: `0.8pt + luma(190)`). +/// - ellipse-fill (fill): Fill used for SD ellipses (default: none). +/// - grid (bool): Show the background grid (default: true). +/// - color-by-vowel (bool): Use a color cycle by vowel category (default: true). +/// - point-size (int): Marker size for synthetic tokens (default: 50). +/// - point-color (color or auto): Override token color; `auto` uses the plot cycle +/// (default: auto). +/// - point-alpha (ratio): Token transparency (default: 20%). +/// - vowel-color (color): Color used for vowel labels (default: black). +/// - vowel-size (length): Font size used for vowel labels (default: 20pt). +/// - vowel-weight (str): Font weight used for vowel labels (default: `"regular"`). +/// - axis-size (length): Font size used for axis labels and tick labels +/// (default: 10pt). +/// - scale (float): Overall scale factor for the figure (default: 1.0). +/// - x-label (content): X-axis label (default: `[F2 (Hz)]`). +/// - y-label (content): Y-axis label (default: `[F1 (Hz)]`). +/// - width (length): Diagram width (default: 10cm). +/// - height (length): Diagram height (default: 7cm). +/// +/// Notes: +/// - Language-name input is checked first. If `vowels` matches a built-in +/// language preset such as `"english"` or `"french"`, that inventory is used. +/// - Otherwise, the input is parsed through `ipa-to-unicode` by default, so +/// tipa-style strings like `"a e E o O i u"` work directly. +/// - In CSV mode, `source: csv("...")` assumes the first row is a header row. +/// - In synthetic mode, ellipses visualize the user-provided spread parameters +/// (`sd`, `sd2`); in CSV mode, they reflect sample standard deviations +/// computed from the observed tokens. +/// +/// Example: +/// ``` +/// #formants("i ɪ e ɛ æ a ə ɔ o ʊ u", sd: 80) +/// ``` +#let _formants_impl( + vowels: none, + source: none, + sd: 60, + sd2: none, + n: 10, + seed: 1, + labels: true, + points: true, + centers: false, + ellipse: false, + ellipse-stroke: 0.8pt + luma(190), + ellipse-fill: none, + grid: true, + color-by-vowel: true, + point-size: 50, + point-color: auto, + point-alpha: 20%, + vowel-color: black, + vowel-size: formants-default-vowel-size, + vowel-weight: "regular", + axis-size: 10pt, + scale: 1.0, + x-label: [F2 (Hz)], + y-label: [F1 (Hz)], + width: 10cm, + height: 7cm, +) = { + let tokens = () + let centroids = () + let spread2 = if sd2 == none { sd } else { sd2 } + let unknown = () + + if source != none { + let parsed = _csv-tokens(source) + if parsed.error != none { + return parsed.error + } + tokens = parsed.tokens + centroids = _centroids-from-tokens(tokens) + if tokens.len() == 0 { + return [*Error:* `source` is empty] + } + } else { + let preset-map = if vowels == "english" { + english-hillenbrand-male + } else { + phonetic-vowel-presets + } + let requested = _normalize-vowel-string(vowels) + let known = requested.filter(vowel => vowel in preset-map) + unknown = _unique(requested.filter(vowel => vowel not in preset-map)) + + if known.len() == 0 { + return _warn([No supported vowels found in input: "#vowels".]) + } + + tokens = _token-cloud(known, preset-map, n, sd, spread2, seed) + centroids = _centroid-pairs(known, preset-map).map(ct => (..ct, sd-f1: sd, sd-f2: spread2)) + } + + let f1-values = tokens.map(t => t.f1) + centroids.map(t => t.f1) + let f2-values = tokens.map(t => t.f2) + centroids.map(t => t.f2) + let pad-f1 = if source != none { + calc.max(calc.max(..centroids.map(ct => ct.sd-f1)) * 1.5, 60) + } else { + calc.max(sd * 1.5, 60) + } + let pad-f2 = if source != none { + calc.max(calc.max(..centroids.map(ct => ct.sd-f2)) * 1.5, 80) + } else { + calc.max(spread2 * 1.5, 80) + } + let f1-min = calc.min(..f1-values) - pad-f1 + let f1-max = calc.max(..f1-values) + pad-f1 + let f2-min = calc.min(..f2-values) - pad-f2 + let f2-max = calc.max(..f2-values) + pad-f2 + let x-step = _nice-tick-step(f2-max - f2-min) + let y-step = _nice-tick-step(f1-max - f1-min) + let x-ticks = _make-ticks(f2-min, f2-max, x-step) + let y-ticks = _make-ticks(f1-min, f1-max, y-step) + let groups = _unique(tokens.map(token => token.vowel)).map(vowel => ( + vowel, + tokens.filter(token => token.vowel == vowel), + )) + + let cycle = if color-by-vowel { + ( + rgb("#3b82f6"), + rgb("#ef4444"), + rgb("#10b981"), + rgb("#f59e0b"), + rgb("#8b5cf6"), + rgb("#ec4899"), + rgb("#14b8a6"), + rgb("#f97316"), + rgb("#06b6d4"), + rgb("#84cc16"), + ) + } else { + (rgb("#64748b"),) + } + + [ + #if unknown.len() > 0 { + _warn([Skipped unsupported vowel(s): #unknown.join(", ").]) + v(0.5em) + } + #context { + let doc-font = text.font + let scaled-width = width * scale + let scaled-height = height * scale + let scaled-point-size = point-size * scale + let scaled-vowel-size = vowel-size * scale + let scaled-axis-size = axis-size * scale + let axis-tick-color = gray.darken(35%) + let scaled-axis-stroke = 0.8pt * scale + axis-tick-color + let scaled-grid-stroke = 0.5pt * scale + luma(220) + let scaled-center-size = 48 * scale + let scaled-center-stroke = 1pt * scale + let tick-pad = scaled-axis-size * 0.35 + show lq.selector(lq.tick-label): it => [] + let x-tick-labels = x-ticks.map(value => text(font: doc-font, size: scaled-axis-size)[#str(value)]) + let y-tick-labels = y-ticks.map(value => text(font: doc-font, size: scaled-axis-size)[#str(value)]) + let max-x-tick-height = calc.max(..x-tick-labels.map(label => measure(label).height)) + let max-y-tick-width = calc.max(..y-tick-labels.map(label => measure(label).width)) + let x-label-pad = max-x-tick-height + scaled-axis-size * 0.9 + let y-label-pad = max-y-tick-width + scaled-axis-size * 1.05 + let axis-label-color = black + let x-axis-label = text(font: doc-font, size: scaled-axis-size, fill: axis-label-color)[#x-label] + let y-axis-label = rotate( + -90deg, + text(font: doc-font, size: scaled-axis-size, fill: axis-label-color)[#y-label], + reflow: true, + ) + lq.diagram( + width: scaled-width, + height: scaled-height, + xlim: (f2-max, f2-min), + ylim: (f1-max, f1-min), + xlabel: none, + ylabel: none, + xscale: "linear", + yscale: "linear", + xaxis: ( + position: top, + stroke: scaled-axis-stroke, + mirror: false, + exponent: none, + tick-distance: x-step, + ), + yaxis: ( + position: right, + stroke: scaled-axis-stroke, + mirror: false, + exponent: none, + tick-distance: y-step, + ), + grid: if grid { scaled-grid-stroke } else { none }, + fill: white, + cycle: cycle, + legend: none, + ..centroids.map(ct => if ellipse { + lq.ellipse( + ct.f2 - ct.sd-f2, + ct.f1 - ct.sd-f1, + width: ct.sd-f2 * 2, + height: ct.sd-f1 * 2, + stroke: ellipse-stroke, + fill: ellipse-fill, + ) + } else { + none + }), + ..groups.map(group => { + let vowel = group.at(0) + let cloud = group.at(1) + if points { + lq.scatter( + cloud.map(token => token.f2), + cloud.map(token => token.f1), + mark: "o", + color: point-color, + size: cloud.map(_ => scaled-point-size), + stroke: none, + alpha: point-alpha, + label: if color-by-vowel { context text(font: phonokit-font.get())[#vowel] } else { none }, + ) + } else { + none + } + }), + ..centroids.map(ct => if centers { + lq.scatter( + (ct.f2,), + (ct.f1,), + mark: "+", + size: (scaled-center-size,), + color: black, + stroke: (paint: black, thickness: scaled-center-stroke), + ) + } else { + none + }), + ..centroids.map(ct => if labels { + lq.place( + ct.f2, + ct.f1, + align: center + horizon, + context text( + font: phonokit-font.get(), + weight: vowel-weight, + size: scaled-vowel-size, + fill: vowel-color, + )[#ct.vowel], + ) + } else { + none + }), + ..x-ticks.map(value => lq.place( + value, + 0%, + align: bottom + center, + pad(bottom: tick-pad, text(font: doc-font, size: scaled-axis-size, fill: axis-tick-color)[#str(value)]), + )), + ..y-ticks.map(value => lq.place( + 100%, + value, + align: left + horizon, + pad(left: tick-pad, text(font: doc-font, size: scaled-axis-size, fill: axis-tick-color)[#str(value)]), + )), + lq.place( + 50%, + 0%, + align: bottom + center, + pad(bottom: x-label-pad, x-axis-label), + ), + lq.place( + 100%, + 50%, + align: left + horizon, + pad(left: y-label-pad, y-axis-label), + ), + ) + } + ] +} + +#let formants(..args) = { + let pos = args.pos() + let named = args.named() + let vowels = if "vowels" in named { named.at("vowels") } else { pos.at(0, default: none) } + + _formants_impl( + vowels: vowels, + source: named.at("source", default: none), + sd: named.at("sd", default: 60), + sd2: named.at("sd2", default: none), + n: named.at("n", default: 10), + seed: named.at("seed", default: 1), + labels: named.at("labels", default: true), + points: named.at("points", default: true), + centers: named.at("centers", default: false), + ellipse: named.at("ellipse", default: false), + ellipse-stroke: named.at("ellipse-stroke", default: 0.8pt + luma(190)), + ellipse-fill: named.at("ellipse-fill", default: none), + grid: named.at("grid", default: true), + color-by-vowel: named.at("color-by-vowel", default: true), + point-size: named.at("point-size", default: 50), + point-color: named.at("point-color", default: auto), + point-alpha: named.at("point-alpha", default: 20%), + vowel-color: named.at("vowel-color", default: black), + vowel-size: named.at("vowel-size", default: formants-default-vowel-size), + vowel-weight: named.at("vowel-weight", default: "regular"), + axis-size: named.at("axis-size", default: 10pt), + scale: named.at("scale", default: 1.0), + x-label: named.at("x-label", default: [F2 (Hz)]), + y-label: named.at("y-label", default: [F1 (Hz)]), + width: named.at("width", default: 10cm), + height: named.at("height", default: 7cm), + ) +} + +#let _vot-label(value, abbreviation) = { + let shown = if calc.abs(value) == calc.round(calc.abs(value)) { + str(int(value)) + } else { + str(value) + } + [#abbreviation: #shown ms] +} + +#let _vot-ui-labels = ( + "en": ( + closure: [closure], + release: [release], + voicing-onset: [voicing onset], + vowel: [vowel], + aspiration: [aspiration], + prevoicing: [prevoicing], + vot-abbr: [VOT], + ), + "fr": ( + closure: [occlusion], + release: [relâchement], + voicing-onset: [début du voisement], + vowel: [voyelle], + aspiration: [aspiration], + prevoicing: [prévoisement], + vot-abbr: [DES], + ), + "pt": ( + closure: [oclusão], + release: [soltura], + voicing-onset: [início do vozeamento], + vowel: [vogal], + aspiration: [aspiração], + prevoicing: [pré-vozeamento], + vot-abbr: [VOT], + ), +) + +#let _vot-label-or-default(label, default) = { + if label == auto { + default + } else { + label + } +} + +#let vot( + vot, + closure: 40, + vowel: 60, + scale: 1.0, + label: auto, + keys: false, + ui-lang: "en", + closure-label: auto, + release-label: auto, + voicing-label: auto, + vowel-label: auto, + vot-label: auto, + interval-label: auto, + interval-key: auto, + closure-segment: none, + interval-segment: none, + vowel-segment: none, + segment-size: 10pt, + fill-closure: luma(230), + fill-vowel: white, + fill-aspiration: luma(245), + voicing: true, + voicing-stroke: auto, +) = context { + assert(type(vot) == int or type(vot) == float, message: "vot must be a number of milliseconds") + assert(type(closure) == int or type(closure) == float, message: "closure must be a number of milliseconds") + assert(type(vowel) == int or type(vowel) == float, message: "vowel must be a number of milliseconds") + let ui-locale = resolve-ui-lang(ui-lang) + if ui-locale == none { + return ui-lang-error(ui-lang) + } + let ui-labels = _vot-ui-labels.at(ui-locale) + + let vot-ms = float(vot) + let closure-ms = calc.max(float(closure), 0.0) + let vowel-ms = calc.max(float(vowel), 1.0) + let release = 0.0 + let voicing-onset-time = vot-ms + let closure-start-time = -closure-ms + calc.min(vot-ms, 0.0) + let left-time = calc.min(closure-start-time, calc.min(vot-ms, 0.0)) + let right-time = calc.max(vot-ms, 0.0) + vowel-ms + let time-span = calc.max(right-time - left-time, 1.0) + let width = calc.max(4.5, time-span * 0.045) + let tx = t => (t - left-time) / time-span * width + let show-label = if label == auto { true } else { label } + let closure-text = _vot-label-or-default(closure-label, ui-labels.closure) + let release-text = _vot-label-or-default(release-label, ui-labels.release) + let voicing-text = _vot-label-or-default(voicing-label, ui-labels.voicing-onset) + let vowel-text = _vot-label-or-default(vowel-label, ui-labels.vowel) + let vot-text = _vot-label-or-default(vot-label, _vot-label(vot-ms, ui-labels.vot-abbr)) + let interval-text = _vot-label-or-default(interval-label, ui-labels.aspiration) + let region-y1 = 0.1 + let region-y2 = 0.85 + let bracket-y = -0.86 + let event-label-y = 1.56 + let event-line-top = if keys { event-label-y } else { region-y2 } + let legend-y = 2.2 + let release-x = tx(release) + let voicing-x = tx(voicing-onset-time) + let vowel-start = calc.max(vot-ms, 0.0) + let scale-factor = scale + let doc-font-size = 8pt * scale-factor + let label-size = 7pt * scale-factor + let segment-font-size = segment-size * scale-factor + let event-size = 6pt * scale-factor + let region-stroke = 0.7pt * scale-factor + black + let release-stroke = ( + paint: black, + thickness: 0.7pt * scale-factor, + dash: "dashed", + cap: "butt", + join: "miter", + ) + let aspiration-stroke = ( + paint: gray.darken(35%), + thickness: 0.7pt * scale-factor, + dash: "dotted", + cap: "butt", + join: "miter", + ) + let wave-stroke = if voicing-stroke == auto { + (paint: gray.darken(45%), thickness: 0.55pt * scale-factor) + } else { + voicing-stroke + } + let waveform-stroke = if type(wave-stroke) == dictionary { + (cap: "round", join: "round", ..wave-stroke) + } else { + wave-stroke + } + let text-label = body => text(size: label-size, font: phonokit-font.get())[#body] + let text-doc = body => text(size: doc-font-size, font: phonokit-font.get())[#body] + let text-segment = body => { + let rendered = if type(body) == str { ipa-to-unicode(body) } else { body } + text(size: segment-font-size, font: phonokit-font.get())[#rendered] + } + let canvas-label-width = body => measure(text-label(body)).width / 1cm / scale-factor + let label-padding = 0.35 + let interval-width = calc.abs(voicing-x - release-x) + let closure-width = calc.abs(release-x - tx(closure-start-time)) + let vowel-width = calc.abs(tx(right-time) - tx(vowel-start)) + let minimum-labelled-interval = 5.0 + let show-closure-label = closure-ms > 0 and canvas-label-width(closure-text) + label-padding <= closure-width + let show-vowel-label = canvas-label-width(vowel-text) + label-padding <= vowel-width + let show-interval-segment = vot-ms >= minimum-labelled-interval and interval-segment != none + let show-positive-interval-label = ( + vot-ms >= minimum-labelled-interval + and interval-label != none + and canvas-label-width(interval-text) + label-padding <= interval-width + ) + let legend-interval = vot-ms > 0 and interval-label != none and not show-positive-interval-label + let interval-key-text = if interval-key != auto { + interval-key + } else if type(interval-text) == str and lower(interval-text).contains("aspir") { + [A] + } else { + [I] + } + let legend-text = { + [R = #release-text; V = #voicing-text] + if legend-interval { + [; #interval-key-text = #interval-text] + } + } + let event-tag = body => box( + width: 0.28cm * scale-factor, + height: 0.28cm * scale-factor, + stroke: region-stroke, + fill: white, + align(center + horizon, text(size: event-size, font: phonokit-font.get())[#body]), + ) + let event-key-width = measure(event-tag(interval-key-text)).width / 1cm / scale-factor + let show-interval-key = keys and legend-interval and event-key-width + label-padding <= interval-width + let region-label-y = region-y2 + 0.24 + let interval-label-y = region-label-y + let region-label-anchor = "base" + let segment-y = -0.22 + let event-line-bottom = bracket-y - 0.12 + + box(inset: 1em, baseline: 50%, cetz.canvas(length: scale-factor * 1cm, { + import cetz.draw: * + + let draw-wave = (x1, x2, y, amp: 0.08, step: 0.025, cycle-width: 0.65) => { + let span = x2 - x1 + if span > step { + let count = int(calc.max(18, calc.floor(span / step))) + let cycles = calc.max(1.0, span / cycle-width) + let points = () + for i in range(count) { + let p = i / (count - 1) + points.push((x1 + p * span, y + amp * calc.sin(p * cycles * 360deg))) + } + line(..points, stroke: waveform-stroke) + } + } + let noise-sample = i => { + (calc.sin(i * 137deg) * 0.55) + (calc.sin(i * 271deg) * 0.32) + (calc.sin(i * 53deg) * 0.18) + } + let draw-noise = (x1, x2, y, amp: 0.055, step: 0.04) => { + let span = x2 - x1 + if span > step { + let count = int(calc.max(8, calc.floor(span / step))) + let points = () + for i in range(count) { + let p = i / (count - 1) + points.push((x1 + p * span, y + amp * noise-sample(i))) + } + line(..points, stroke: waveform-stroke) + } + } + let draw-prevoicing = (x1, x2, y, amp: 0.035, step: 0.04, cycle-width: 0.32) => { + let span = x2 - x1 + if span > step { + let count = int(calc.max(10, calc.floor(span / step))) + let cycles = calc.max(1.0, span / cycle-width) + let points = () + for i in range(count) { + let p = i / (count - 1) + let modulation = 0.75 + 0.25 * calc.sin(p * 137deg) + let sample = ( + 0.45 * modulation * calc.sin(p * cycles * 360deg) + + 0.55 * calc.sin(p * cycles * 900deg + 37deg) + + 0.42 * calc.sin(p * cycles * 1450deg + 83deg) + + 0.34 * noise-sample(i) + ) + points.push((x1 + p * span, y + amp * sample)) + } + line(..points, stroke: waveform-stroke) + } + } + let draw-prevoicing-transition = (x1, x2, y, step: 0.04, cycle-width: 0.36) => { + let span = x2 - x1 + if span > step { + let count = int(calc.max(8, calc.floor(span / step))) + let cycles = calc.max(1.0, span / cycle-width) + let points = () + for i in range(count) { + let p = i / (count - 1) + let closure = (1.0 - p) * 0.008 * calc.sin(p * 180deg) + let prevoice = ( + p + * 0.035 + * ( + 0.45 * calc.sin(p * cycles * 360deg) + + 0.55 * calc.sin(p * cycles * 900deg + 37deg) + + 0.42 * calc.sin(p * cycles * 1450deg + 83deg) + + 0.34 * noise-sample(i) + ) + ) + points.push((x1 + p * span, y + closure + prevoice)) + } + line(..points, stroke: waveform-stroke) + } + } + let draw-waveforms = () => { + if voicing { + if closure-ms > 0 { + if vot-ms < 0 { + let transition-width = calc.min(voicing-x - tx(closure-start-time), 0.32) + let transition-start-x = voicing-x - transition-width + draw-wave( + tx(closure-start-time), + transition-start-x, + region-y1 + 0.16, + amp: 0.008, + step: 0.07, + cycle-width: 1.2, + ) + draw-prevoicing-transition(transition-start-x, voicing-x, region-y1 + 0.16) + draw-prevoicing(voicing-x, release-x, region-y1 + 0.16) + } else { + draw-wave(tx(closure-start-time), release-x, region-y1 + 0.16, amp: 0.008, step: 0.07, cycle-width: 1.2) + } + } + if vot-ms > 0 { + draw-noise(release-x, voicing-x, region-y1 + 0.16) + } + draw-wave(tx(vowel-start), tx(right-time), region-y1 + 0.16) + } + } + + if vot-ms > 0 { + rect( + (release-x, region-y1), + (voicing-x, region-y2), + fill: fill-aspiration, + stroke: none, + ) + line((release-x, region-y2), (voicing-x, region-y2), stroke: aspiration-stroke) + line((release-x, region-y1), (voicing-x, region-y1), stroke: aspiration-stroke) + } + + line((release-x, event-line-bottom), (release-x, event-line-top), stroke: release-stroke) + if vot-ms != 0 { + line((voicing-x, event-line-bottom), (voicing-x, event-line-top), stroke: release-stroke) + } + + if closure-ms > 0 { + rect( + (tx(closure-start-time), region-y1), + (release-x, region-y2), + fill: fill-closure, + stroke: region-stroke, + ) + if show-closure-label { + content( + ((tx(closure-start-time) + release-x) / 2, region-label-y), + text-label(closure-text), + anchor: region-label-anchor, + ) + } + if closure-segment != none { + let closure-segment-right-x = if vot-ms < 0 { voicing-x } else { release-x } + content( + ((tx(closure-start-time) + closure-segment-right-x) / 2, segment-y), + text-segment(closure-segment), + anchor: "center", + ) + } + } + + if vot-ms > 0 { + if show-positive-interval-label { + content( + ((release-x + voicing-x) / 2, interval-label-y), + text-label(interval-text), + anchor: region-label-anchor, + ) + } else if interval-label != none and show-interval-key { + content( + ((release-x + voicing-x) / 2, interval-label-y), + event-tag(interval-key-text), + anchor: "center", + ) + } + if show-interval-segment { + content( + ((release-x + voicing-x) / 2, segment-y), + text-segment(interval-segment), + anchor: "center", + ) + } + } + + rect( + (tx(vowel-start), region-y1), + (tx(right-time), region-y2), + fill: fill-vowel, + stroke: region-stroke, + ) + if show-vowel-label { + content( + ((tx(vowel-start) + tx(right-time)) / 2, region-label-y), + text-label(vowel-text), + anchor: region-label-anchor, + ) + } + if vowel-segment != none { + content( + ((tx(vowel-start) + tx(right-time)) / 2, segment-y), + text-segment(vowel-segment), + anchor: "center", + ) + } + + if keys { + content( + (release-x, event-label-y), + event-tag([R]), + anchor: "center", + ) + if vot-ms != 0 { + content( + (voicing-x, event-label-y), + event-tag([V]), + anchor: "center", + ) + } + content( + (tx(right-time), legend-y), + text-doc(legend-text), + anchor: "east", + ) + } + + draw-waveforms() + + if show-label { + if vot-ms == 0 { + content( + (release-x, bracket-y - 0.35), + text-doc(vot-text), + anchor: "center", + ) + } else { + let start-x = calc.min(release-x, voicing-x) + let end-x = calc.max(release-x, voicing-x) + line((start-x, bracket-y), (end-x, bracket-y), stroke: region-stroke) + line((start-x, bracket-y - 0.12), (start-x, bracket-y + 0.12), stroke: region-stroke) + line((end-x, bracket-y - 0.12), (end-x, bracket-y + 0.12), stroke: region-stroke) + content( + ((start-x + end-x) / 2, bracket-y - 0.35), + text-doc(vot-text), + anchor: "center", + ) + } + } + })) +} diff --git a/packages/preview/phonokit/0.5.11/prosody.typ b/packages/preview/phonokit/0.5.11/prosody.typ new file mode 100644 index 0000000000..ea89ad518e --- /dev/null +++ b/packages/preview/phonokit/0.5.11/prosody.typ @@ -0,0 +1,2200 @@ +#import "@preview/cetz:0.5.2" +#import "ipa.typ": ipa-to-unicode +#import "_config.typ": phonokit-font + +// Follows the same spacing convention as #ipa(): +// - Backslash commands need spaces: "t \\ae p" → "tæp" +// - Single characters don't: "SIp" → "ʃɪp" +#let convert-prosody-input(input) = { + let result = "" + let buffer = "" + let chars = input.codepoints() + let i = 0 + + while i < chars.len() { + let char = chars.at(i) + + // Check if this is a structural marker + // Note: ' and , are stress markers (not printed, just for structure) + if char in ("'", ",", ".", "(", ")") { + // First, process any buffered content + if buffer != "" { + result += ipa-to-unicode(buffer) + buffer = "" + } + // Add the structural marker (will be stripped during parsing) + result += char + } else { + // Add to buffer (including spaces, which will be handled by ipa-to-unicode) + buffer += char + } + + i += 1 + } + + // Process any remaining buffer + if buffer != "" { + result += ipa-to-unicode(buffer) + } + + result +} + +#let is-vowel(cluster) = { + // Check the base character (first codepoint) to handle both + // precomposed vowels (like "ã") and combining forms (like "a" + "̃") + let base = cluster.codepoints().at(0) + + // Check if base is a vowel + let base-is-vowel = ( + base + in ("a", "e", "i", "o", "u", "ɚ", "ɝ", "ɯ", "ɐ", "ɒ", "æ", "ɛ", "ɪ", "ɔ", "ø", "œ", "ɨ", "ʉ", "ʊ", "ə", "ʌ", "ɑ") + ) + + // Also check for diphthongs and precomposed forms as complete clusters + let cluster-is-vowel = cluster in ("aɪ", "eɪ", "oɪ", "aʊ", "oʊ", "ã", "ẽ", "õ", "ɛ̃", "ɔ̃", "œ̃", "ɑ̃") + + // Check if cluster contains syllabicity marker (̩ U+0329) + // Syllabic consonants (like m̩, n̩, l̩) function as vowels/nuclei + let is-syllabic = cluster.contains("̩") + + base-is-vowel or cluster-is-vowel or is-syllabic +} + +// Custom clustering that handles: +// 1. Affricates (tie bar U+0361) - merge "t͡" + "ʃ" → "t͡ʃ" +// 2. Spacing modifiers (like "ʰ") - merge "b" + "ʰ" → "bʰ" +// 3. Length marks (ː) - merge "a" + "ː" → "aː" as atomic unit +#let smart-clusters(text) = { + let basic-clusters = text.clusters() + let result = () + let i = 0 + + // Define spacing modifiers that should merge with preceding segment + let spacing-modifiers = ("ʰ", "ʷ", "ʲ", "ˠ", "ˤ", "̚") + // Length mark (U+02D0) + let length-mark = "ː" + + while i < basic-clusters.len() { + let current = basic-clusters.at(i) + + // Check if this cluster contains a tie bar + if current.contains("͡") { + // This cluster has a tie bar - merge with next cluster (affricate) + if i + 1 < basic-clusters.len() { + result.push(current + basic-clusters.at(i + 1)) + i += 2 // Skip both clusters + } else { + result.push(current) + i += 1 + } + } else if i + 1 < basic-clusters.len() and basic-clusters.at(i + 1) in spacing-modifiers { + // Next cluster is a spacing modifier - merge with current + result.push(current + basic-clusters.at(i + 1)) + i += 2 // Skip both clusters + } else if i + 1 < basic-clusters.len() and basic-clusters.at(i + 1) == length-mark { + // Next cluster is a length mark - merge with current (atomic long vowel) + result.push(current + basic-clusters.at(i + 1)) + i += 2 // Skip both clusters + } else { + // Regular cluster + result.push(current) + i += 1 + } + } + + result +} + +#let parse-syllable(syll) = { + // Use smart-clusters() to properly handle affricates and combining diacritics + let clusters = smart-clusters(syll) + let onset = "" + let nucleus = "" + let coda = "" + let found-nucleus = false + + for cluster in clusters { + if is-vowel(cluster) { + nucleus += cluster + found-nucleus = true + } else if not found-nucleus { + onset += cluster + } else { + coda += cluster + } + } + + (onset: onset, nucleus: nucleus, coda: coda) +} + +// Helper function to draw syllable internal structure +#let draw-syllable-structure( + x-offset, + sigma-y, + syll, + terminal-y, + diagram-scale: 1.0, + geminate-coda-x: none, + geminate-onset-x: none, + geminate-coda-text: none, + geminate-onset-text: none, + compact: false, + or-y: none, + n-y: none, +) = { + import cetz.draw: * + + // O/R and N/C level positions (defaults preserve original hardcoded offsets) + let or-level = if or-y == none { sigma-y - 0.75 } else { or-y } + let n-level = if n-y == none { sigma-y - 1.65 } else { n-y } + + // Choose offset values based on compact mode + let (line-offset, text-offset) = if compact { + (0.70, 0.40) // Compact: shorter lines, raised segments + } else { + (0.30, 0) // Standard: longer lines, lower segments + } + + let has-onset = syll.onset != "" + let has-coda = syll.coda != "" + + // Calculate segment counts for adaptive spacing + // Use smart-clusters() to properly count segments including affricates + let onset-segments = if has-onset { smart-clusters(syll.onset) } else { () } + let num-onset = if has-onset { onset-segments.len() } else { 0 } + + let nucleus-segments = smart-clusters(syll.nucleus) + let num-nucleus = nucleus-segments.len() + + let coda-segments = if has-coda { smart-clusters(syll.coda) } else { () } + let num-coda = if has-coda { coda-segments.len() } else { 0 } + + let segment-spacing = 0.35 + let min-gap = 0.75 + + // Headedness: Rhyme is head of syllable, Nucleus is head of Rhyme + // Heads align vertically, non-heads are angled + let rhyme-x = x-offset // vertical (head of syllable) + let nucleus-x = rhyme-x // MUST stay at rhyme-x (vertical, head of rhyme) + + // Adaptive positioning for onset (move left if many segments) + let onset-x = if has-onset { + let min-offset = (num-onset - 1) * segment-spacing / 2 + (num-nucleus - 1) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { x-offset - min-offset } else { x-offset - default-offset } + } else { + x-offset + } + + // Adaptive positioning for coda (move right to avoid nucleus segments) + let coda-x = if has-coda { + let min-offset = (num-nucleus + num-coda - 2) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { rhyme-x + min-offset } else { rhyme-x + default-offset } + } else { + rhyme-x + } + + // Branches from syllable + if has-onset { + line((x-offset, sigma-y + 0.25), (onset-x, or-level + 0.30)) + content((onset-x, or-level), context text(size: 10 * diagram-scale * 1pt, font: phonokit-font.get())[O]) + + // Branch to each onset segment + let onset-segments = smart-clusters(syll.onset) + let num-onset = onset-segments.len() + + // Always draw all segments, but handle geminates specially + let onset-total-width = (num-onset - 1) * segment-spacing + let onset-start-x = onset-x - onset-total-width / 2 + + for (i, segment) in onset-segments.enumerate() { + let seg-x = onset-start-x + i * segment-spacing + + // Check if this segment is the geminate + let is-geminate = (geminate-onset-x != none and segment == geminate-onset-text) + + if is-geminate { + // Geminate: draw line to geminate position (text drawn separately in geminate section) + line((onset-x, or-level - 0.35), (geminate-onset-x, terminal-y + line-offset)) + } else { + // Normal segment: draw line and text + line((onset-x, or-level - 0.35), (seg-x, terminal-y + line-offset)) + content( + (seg-x, terminal-y + text-offset), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + } + + // Rhyme branch + line((x-offset, sigma-y + 0.25), (rhyme-x, or-level + 0.30)) + content((rhyme-x, or-level), context text(size: 10 * diagram-scale * 1pt, font: phonokit-font.get())[R]) + + // Nucleus + line((rhyme-x, or-level - 0.35), (nucleus-x, n-level + 0.30)) + content((nucleus-x, n-level), context text(size: 10 * diagram-scale * 1pt, font: phonokit-font.get())[N]) + + // Branch to each nucleus segment + let nucleus-total-width = (num-nucleus - 1) * segment-spacing + let nucleus-start-x = nucleus-x - nucleus-total-width / 2 + + for (i, segment) in nucleus-segments.enumerate() { + let seg-x = nucleus-start-x + i * segment-spacing + line((nucleus-x, n-level - 0.25), (seg-x, terminal-y + line-offset)) + content( + (seg-x, terminal-y + text-offset), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + + // Coda (if exists) + if has-coda { + line((rhyme-x, or-level - 0.35), (coda-x, n-level + 0.30)) + content((coda-x, n-level), context text(size: 10 * diagram-scale * 1pt, font: phonokit-font.get())[C]) + + // Branch to each coda segment + // Always draw all segments, but handle geminates specially + let coda-total-width = (num-coda - 1) * segment-spacing + let coda-start-x = coda-x - coda-total-width / 2 + + for (i, segment) in coda-segments.enumerate() { + let seg-x = coda-start-x + i * segment-spacing + + // Check if this segment is the geminate + let is-geminate = (geminate-coda-x != none and segment == geminate-coda-text) + + if is-geminate { + // Geminate: draw line to geminate position (text drawn separately in geminate section) + line((coda-x, n-level - 0.25), (geminate-coda-x, terminal-y + line-offset)) + } else { + // Normal segment: draw line and text + line((coda-x, n-level - 0.25), (seg-x, terminal-y + line-offset)) + content( + (seg-x, terminal-y + text-offset), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + } +} + +// Visualizes a single syllable's internal structure (On/Rh/Nu/Co) +// Now accepts IPA-style input like "k a" or "'t a" +#let syllable(input, scale: 1.0, symbol: ("σ",), distance: none) = { + // Check for syllable boundary markers + if input.contains(".") { + return text(fill: red, weight: "bold")[⚠ Warning: For more than one syllable, use \#foot() or \#word().] + } + + // Check for problematic diacritic sequences + let problematic-sequences = ("''", ",,", "\\* \\*", "\\t \\t", "::", "((", "))") + for seq in problematic-sequences { + if input.contains(seq) { + return text(fill: red, weight: "bold")[⚠ Warning: Problematic sequence involving diacritics: "#seq"] + } + } + + // Convert IPA-style input to Unicode + let converted = convert-prosody-input(input) + + // Parse a single syllable + // Strip ALL stress markers (' for primary, , for secondary) regardless of position + let stressed = converted.starts-with("'") or converted.starts-with(",") + let clean-input = converted.replace("'", "").replace(",", "") + + let parsed = parse-syllable(clean-input) + + // Check for too many codas (limit: 5 to avoid crossing lines) + let coda-segments-temp = if parsed.coda != "" { smart-clusters(parsed.coda) } else { () } + if coda-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many coda consonants (max 5 to avoid line crossings). Found: #coda-segments-temp.len()] + } + + // Check for too many onsets (limit: 5 to avoid crossing lines) + let onset-segments-temp = if parsed.onset != "" { smart-clusters(parsed.onset) } else { () } + if onset-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many onset consonants (max 5 to avoid line crossings). Found: #onset-segments-temp.len()] + } + + // Check for too many nucleus segments (limit: 5) + let nucleus-segments-temp = smart-clusters(parsed.nucleus) + if nucleus-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many nucleus segments (max 5 to avoid line crossings). Found: #nucleus-segments-temp.len()] + } + let syll = ( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: stressed, + ) + + let sym_syll = symbol.at(0, default: "σ") + + let diagram-scale = scale + box(baseline: 50%, cetz.canvas(length: 1cm * diagram-scale, { + import cetz.draw: * + set-style(stroke: 0.7 * diagram-scale * 1pt) + + // Distance multiplier lookup (floor: 1.0 for all sub-syllable levels) + let dist-mult(level) = { + let result = 1.0 + if distance != none { + for entry in distance { + if entry.at(0) == level { + result = calc.max(1.0, entry.at(1)) + } + } + } + result + } + + let sigma-y = 0 + let x-offset = 0 + + // Sub-syllable level positions (0=σ→O/R, 1=O/R→N/C, 2=N/C→segments) + let or-level = sigma-y - 0.75 * dist-mult(0) + let n-level = or-level - 1.25 * dist-mult(1) + let terminal-y = n-level - 1.50 * dist-mult(2) + + // Standalone syllable spacing: Nu/Co positioned lower (halfway between Rh and segments) + let line-offset = 0.70 + let text-offset = 0.40 + + let has-onset = syll.onset != "" + let has-coda = syll.coda != "" + + // Calculate segment counts for adaptive spacing + let onset-segments = if has-onset { smart-clusters(syll.onset) } else { () } + let num-onset = if has-onset { onset-segments.len() } else { 0 } + + let nucleus-segments = smart-clusters(syll.nucleus) + let num-nucleus = nucleus-segments.len() + + let coda-segments = if has-coda { smart-clusters(syll.coda) } else { () } + let num-coda = if has-coda { coda-segments.len() } else { 0 } + + let segment-spacing = 0.35 + let min-gap = 0.75 + + // Headedness: Rhyme is head of syllable, Nucleus is head of Rhyme + let rhyme-x = x-offset + let nucleus-x = rhyme-x + + // Adaptive positioning for onset + let onset-x = if has-onset { + let min-offset = (num-onset - 1) * segment-spacing / 2 + (num-nucleus - 1) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { x-offset - min-offset } else { x-offset - default-offset } + } else { + x-offset + } + + // Adaptive positioning for coda + let coda-x = if has-coda { + let min-offset = (num-nucleus + num-coda - 2) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { rhyme-x + min-offset } else { rhyme-x + default-offset } + } else { + rhyme-x + } + + // Syllable node + content((x-offset, sigma-y + 0.54), context text( + size: 12 * diagram-scale * 1pt, + font: phonokit-font.get(), + )[#sym_syll]) + + // Onset branches (if exists) + if has-onset { + line((x-offset, sigma-y + 0.25), (onset-x, or-level + 0.30)) + content((onset-x, or-level), context text(size: 10 * diagram-scale * 1pt, font: phonokit-font.get())[O]) + + let onset-total-width = (num-onset - 1) * segment-spacing + let onset-start-x = onset-x - onset-total-width / 2 + + for (i, segment) in onset-segments.enumerate() { + let seg-x = onset-start-x + i * segment-spacing + line((onset-x, or-level - 0.35), (seg-x, terminal-y + line-offset)) + content( + (seg-x, terminal-y + text-offset), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + + // Rhyme branch + line((x-offset, sigma-y + 0.25), (rhyme-x, or-level + 0.30)) + content((rhyme-x, or-level), context text(size: 10 * diagram-scale * 1pt, font: phonokit-font.get())[R]) + + // Nucleus + line((rhyme-x, or-level - 0.35), (nucleus-x, n-level + 0.35)) + content((nucleus-x, n-level), context text(size: 10 * diagram-scale * 1pt, font: phonokit-font.get())[N]) + + // Branch to each nucleus segment + let nucleus-total-width = (num-nucleus - 1) * segment-spacing + let nucleus-start-x = nucleus-x - nucleus-total-width / 2 + + for (i, segment) in nucleus-segments.enumerate() { + let seg-x = nucleus-start-x + i * segment-spacing + line((nucleus-x, n-level - 0.25), (seg-x, terminal-y + line-offset)) + content( + (seg-x, terminal-y + text-offset), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + + // Coda (if exists) + if has-coda { + line((rhyme-x, or-level - 0.35), (coda-x, n-level + 0.35)) + content((coda-x, n-level), context text(size: 10 * diagram-scale * 1pt, font: phonokit-font.get())[C]) + + let coda-total-width = (num-coda - 1) * segment-spacing + let coda-start-x = coda-x - coda-total-width / 2 + + for (i, segment) in coda-segments.enumerate() { + let seg-x = coda-start-x + i * segment-spacing + line((coda-x, n-level - 0.25), (seg-x, terminal-y + line-offset)) + content( + (seg-x, terminal-y + text-offset), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + })) +} + +// Visualizes moraic structure (Hyman 1976) +// Onsets: non-moraic (connect directly to σ) +// Nucleus: always moraic (connects to μ, which connects to σ) +// Coda: optionally moraic (coda: false → connects to σ; coda: true → connects to μ) +#let mora(input, coda: false, scale: 1.0, symbol: ("σ", "μ"), distance: none) = { + // Check for syllable boundary markers + if input.contains(".") { + return text(fill: red, weight: "bold")[⚠ Warning: For more than one syllable, use \#foot() or \#word().] + } + + // Check for problematic diacritic sequences + let problematic-sequences = ("''", ",,", "\\* \\*", "\\t \\t", "::", "((", "))") + for seq in problematic-sequences { + if input.contains(seq) { + return text(fill: red, weight: "bold")[⚠ Warning: Problematic sequence involving diacritics: "#seq"] + } + } + + // Convert IPA-style input to Unicode + let converted = convert-prosody-input(input) + + // Parse syllable + // Strip ALL stress markers (' for primary, , for secondary) regardless of position + let clean-input = converted.replace("'", "").replace(",", "") + + let parsed = parse-syllable(clean-input) + + // Check for too many codas (limit: 5 to avoid crossing lines) + let coda-segments-temp = if parsed.coda != "" { smart-clusters(parsed.coda) } else { () } + if coda-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many coda consonants (max 5 to avoid line crossings). Found: #coda-segments-temp.len()] + } + + // Check for too many onsets (limit: 5 to avoid crossing lines) + let onset-segments-temp = if parsed.onset != "" { smart-clusters(parsed.onset) } else { () } + if onset-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many onset consonants (max 5 to avoid line crossings). Found: #onset-segments-temp.len()] + } + + // Check for too many nucleus segments (limit: 5) + let nucleus-segments-temp = smart-clusters(parsed.nucleus) + if nucleus-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many nucleus segments (max 5 to avoid line crossings). Found: #nucleus-segments-temp.len()] + } + + let sym_syll = symbol.at(0, default: "σ") + let sym_mora = symbol.at(1, default: "μ") + + let diagram-scale = scale + box(baseline: 50%, cetz.canvas(length: 1cm * diagram-scale, { + import cetz.draw: * + set-style(stroke: 0.7 * diagram-scale * 1pt) + + // Distance multiplier lookup (floor: 0.5 for all mora levels) + let dist-mult(level) = { + let result = 1.0 + if distance != none { + for entry in distance { + if entry.at(0) == level { + result = calc.max(0.5, entry.at(1)) + } + } + } + result + } + + let sigma-y = 0 + let segment-spacing = 0.35 + let x-offset = 0 + + // Syllable node + content((x-offset, sigma-y + 0.54), context text( + size: 12 * diagram-scale * 1pt, + font: phonokit-font.get(), + )[#sym_syll]) + + // Calculate segment counts + let onset-segments = if parsed.onset != "" { smart-clusters(parsed.onset) } else { () } + let nucleus-segments = smart-clusters(parsed.nucleus) + let coda-segments = if parsed.coda != "" { smart-clusters(parsed.coda) } else { () } + + let num-onset = onset-segments.len() + let num-nucleus = nucleus-segments.len() + let num-coda = coda-segments.len() + + // Position calculations (σ→μ = level 0, μ→segments = level 1) + let mora-base-gap = 1.62 + let terminal-y = sigma-y + 0.35 - mora-base-gap * (dist-mult(0) + dist-mult(1)) + let mora-y = sigma-y + 0.54 - mora-base-gap * 1.4 * dist-mult(0) + let nucleus-mora-x = x-offset + + // Onset position (left of nucleus mora) + // Adaptive positioning: move left based on number of segments to avoid crossings + let onset-x = if num-onset > 0 { + let min-offset = (num-onset - 1) * segment-spacing / 2 + 0.8 + let default-offset = 1.2 + nucleus-mora-x - calc.max(min-offset, default-offset) + } else { + nucleus-mora-x + } + + // Coda position (right of nucleus mora) + // Adaptive positioning: move right based on number of segments to avoid crossings + let coda-x = if num-coda > 0 { + let min-offset = (num-coda - 1) * segment-spacing / 2 + 0.8 + let default-offset = 1.2 + nucleus-mora-x + calc.max(min-offset, default-offset) + } else { + nucleus-mora-x + } + + // Draw ONSET (non-moraic - connects directly to σ) + if num-onset > 0 { + let onset-total-width = (num-onset - 1) * segment-spacing + let onset-start-x = onset-x - onset-total-width / 2 + + for (i, segment) in onset-segments.enumerate() { + let seg-x = onset-start-x + i * segment-spacing + line((x-offset, sigma-y + 0.25), (seg-x, terminal-y + 0.30)) + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + + // Check if nucleus contains long vowel (Vː) + let has-long-vowel = parsed.nucleus.contains("ː") + + // Draw NUCLEUS MORA(E) - one mora for short vowel, two for long vowel + if has-long-vowel { + // Long vowel: draw TWO morae that branch from σ and converge on Vː + let mora-spacing = 0.6 + let mora1-x = nucleus-mora-x - mora-spacing / 2 + let mora2-x = nucleus-mora-x + mora-spacing / 2 + + // Draw two μ nodes + content((mora1-x, mora-y), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#sym_mora]) + content((mora2-x, mora-y), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#sym_mora]) + + // Lines from σ to both morae + line((x-offset, sigma-y + 0.25), (mora1-x, mora-y + 0.35)) + line((x-offset, sigma-y + 0.25), (mora2-x, mora-y + 0.35)) + + // Both morae converge to the long vowel segment + let nucleus-total-width = (num-nucleus - 1) * segment-spacing + let nucleus-start-x = nucleus-mora-x - nucleus-total-width / 2 + + for (i, segment) in nucleus-segments.enumerate() { + let seg-x = nucleus-start-x + i * segment-spacing + // Lines from both morae converge to the segment + line((mora1-x, mora-y - 0.35), (seg-x, terminal-y + 0.30)) + line((mora2-x, mora-y - 0.35), (seg-x, terminal-y + 0.30)) + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } else { + // Short vowel: one mora + content((nucleus-mora-x, mora-y), context text( + size: 12 * diagram-scale * 1pt, + font: phonokit-font.get(), + )[#sym_mora]) + line((x-offset, sigma-y + 0.25), (nucleus-mora-x, mora-y + 0.35)) + + // Draw nucleus segments below mora + let nucleus-total-width = (num-nucleus - 1) * segment-spacing + let nucleus-start-x = nucleus-mora-x - nucleus-total-width / 2 + + for (i, segment) in nucleus-segments.enumerate() { + let seg-x = nucleus-start-x + i * segment-spacing + line((nucleus-mora-x, mora-y - 0.35), (seg-x, terminal-y + 0.30)) + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + + // Draw CODA + if num-coda > 0 { + if coda { + // Moraic coda: ONE μ for all coda segments (they share the mora) + // Draw the coda μ + content((coda-x, mora-y), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#sym_mora]) + + // Line from σ to coda μ + line((x-offset, sigma-y + 0.25), (coda-x, mora-y + 0.35)) + + // All coda segments branch from this single μ + let coda-total-width = (num-coda - 1) * segment-spacing + let coda-start-x = coda-x - coda-total-width / 2 + + for (i, segment) in coda-segments.enumerate() { + let seg-x = coda-start-x + i * segment-spacing + + // Line from shared μ to each segment + line((coda-x, mora-y - 0.35), (seg-x, terminal-y + 0.30)) + + // Draw segment + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } else { + // Non-moraic coda: connects directly to σ + let coda-total-width = (num-coda - 1) * segment-spacing + let coda-start-x = coda-x - coda-total-width / 2 + + for (i, segment) in coda-segments.enumerate() { + let seg-x = coda-start-x + i * segment-spacing + line((x-offset, sigma-y + 0.25), (seg-x, terminal-y + 0.30)) + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + } + })) +} + +// Helper function to draw moraic structure (used by foot.mora and word.mora) +#let draw-moraic-structure( + x-offset, + sigma-y, + syll, + terminal-y, + coda: false, + diagram-scale: 1.0, + geminate-coda-x: none, + geminate-onset-x: none, + mora-symbol: "μ", + mora-y-override: none, +) = { + import cetz.draw: * + + let segment-spacing = 0.35 + + // Calculate segment counts + let onset-segments = if syll.onset != "" { smart-clusters(syll.onset) } else { () } + let nucleus-segments = smart-clusters(syll.nucleus) + let coda-segments = if syll.coda != "" { smart-clusters(syll.coda) } else { () } + + let num-onset = onset-segments.len() + let num-nucleus = nucleus-segments.len() + let num-coda = coda-segments.len() + + // Check if nucleus contains long vowel (needed for spacing calculations) + let has-long-vowel = syll.nucleus.contains("ː") + + // Position calculations + let mora-y = if mora-y-override != none { mora-y-override } else { 0.4 * (sigma-y + 0.54) + 0.6 * terminal-y } + let nucleus-mora-x = x-offset + + // Adaptive onset position (same formula as draw-syllable-structure for uniform spacing) + let onset-x = if num-onset > 0 { + let min-gap = 0.75 + let min-offset = (num-onset - 1) * segment-spacing / 2 + (num-nucleus - 1) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { nucleus-mora-x - min-offset } else { nucleus-mora-x - default-offset } + } else { + nucleus-mora-x + } + + // Adaptive coda position (same formula as draw-syllable-structure for uniform spacing) + let coda-x = if num-coda > 0 { + let min-gap = 0.75 + let min-offset = (num-nucleus + num-coda - 2) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { nucleus-mora-x + min-offset } else { nucleus-mora-x + default-offset } + } else { + nucleus-mora-x + } + + // Draw ONSET (non-moraic - connects directly to σ) + if num-onset > 0 { + // Check if this is a geminate onset + if geminate-onset-x != none { + // Geminate: draw line to geminate position + line((x-offset, sigma-y + 0.25), (geminate-onset-x, terminal-y + 0.30)) + } else { + // Normal onset: draw branches to individual segments + let onset-total-width = (num-onset - 1) * segment-spacing + let onset-start-x = onset-x - onset-total-width / 2 + + for (i, segment) in onset-segments.enumerate() { + let seg-x = onset-start-x + i * segment-spacing + line((x-offset, sigma-y + 0.25), (seg-x, terminal-y + 0.30)) + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + } + + // Draw NUCLEUS MORA(E) + if has-long-vowel { + // Long vowel: TWO morae + let mora-spacing = 0.6 + let mora1-x = nucleus-mora-x - mora-spacing / 2 + let mora2-x = nucleus-mora-x + mora-spacing / 2 + + content((mora1-x, mora-y), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#mora-symbol]) + content((mora2-x, mora-y), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#mora-symbol]) + + line((x-offset, sigma-y + 0.25), (mora1-x, mora-y + 0.35)) + line((x-offset, sigma-y + 0.25), (mora2-x, mora-y + 0.35)) + + let nucleus-total-width = (num-nucleus - 1) * segment-spacing + let nucleus-start-x = nucleus-mora-x - nucleus-total-width / 2 + + for (i, segment) in nucleus-segments.enumerate() { + let seg-x = nucleus-start-x + i * segment-spacing + line((mora1-x, mora-y - 0.35), (seg-x, terminal-y + 0.30)) + line((mora2-x, mora-y - 0.35), (seg-x, terminal-y + 0.30)) + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } else { + // Short vowel: ONE mora + content((nucleus-mora-x, mora-y), context text( + size: 12 * diagram-scale * 1pt, + font: phonokit-font.get(), + )[#mora-symbol]) + line((x-offset, sigma-y + 0.25), (nucleus-mora-x, mora-y + 0.35)) + + let nucleus-total-width = (num-nucleus - 1) * segment-spacing + let nucleus-start-x = nucleus-mora-x - nucleus-total-width / 2 + + for (i, segment) in nucleus-segments.enumerate() { + let seg-x = nucleus-start-x + i * segment-spacing + line((nucleus-mora-x, mora-y - 0.35), (seg-x, terminal-y + 0.30)) + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + + // Draw CODA + if num-coda > 0 { + if coda { + // Moraic coda: ONE μ shared by all segments + content((coda-x, mora-y), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#mora-symbol]) + line((x-offset, sigma-y + 0.25), (coda-x, mora-y + 0.35)) + + // Check if this is a geminate coda + if geminate-coda-x != none { + // Geminate: draw line to geminate position + line((coda-x, mora-y - 0.35), (geminate-coda-x, terminal-y + 0.30)) + } else { + // Normal coda: all segments branch from shared μ + let coda-total-width = (num-coda - 1) * segment-spacing + let coda-start-x = coda-x - coda-total-width / 2 + + for (i, segment) in coda-segments.enumerate() { + let seg-x = coda-start-x + i * segment-spacing + line((coda-x, mora-y - 0.35), (seg-x, terminal-y + 0.30)) + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + } else { + // Non-moraic coda: connects directly to σ + // Check if this is a geminate coda + if geminate-coda-x != none { + // Geminate: draw line to geminate position + line((x-offset, sigma-y + 0.25), (geminate-coda-x, terminal-y + 0.30)) + } else { + // Normal coda: branches to individual segments + let coda-total-width = (num-coda - 1) * segment-spacing + let coda-start-x = coda-x - coda-total-width / 2 + + for (i, segment) in coda-segments.enumerate() { + let seg-x = coda-start-x + i * segment-spacing + line((x-offset, sigma-y + 0.25), (seg-x, terminal-y + 0.30)) + content( + (seg-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#segment], + anchor: "north", + ) + } + } + } + } +} + +// Visualizes foot and syllable levels +// Now accepts IPA-style input like "k a.'v a.l o" +#let foot(input, scale: 1.0, symbol: ("Σ", "σ"), distance: none) = { + // Check for parentheses (foot should not have multiple feet) + if input.contains("(") or input.contains(")") { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: foot() is for a single foot. If you need multiple feet, use word() instead.] + } + + // Convert IPA-style input to Unicode + let converted = convert-prosody-input(input) + + // Parse syllables from dotted input + let syllables = () + let buffer = "" + let chars = converted.codepoints() + let i = 0 + + while i < chars.len() { + let char = chars.at(i) + + if char == "." { + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + buffer = "" + } + } else { + buffer += char + } + i += 1 + } + + // Handle remaining buffer + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + } + + // Check for too many onsets/codas/nucleus in any syllable (limit: 5 to avoid crossing lines) + for (i, syll) in syllables.enumerate() { + let coda-segments-temp = if syll.coda != "" { smart-clusters(syll.coda) } else { () } + if coda-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many coda consonants in syllable #(i + 1) (max 5 to avoid line crossings). Found: #coda-segments-temp.len()] + } + let onset-segments-temp = if syll.onset != "" { smart-clusters(syll.onset) } else { () } + if onset-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many onset consonants in syllable #(i + 1) (max 5 to avoid line crossings). Found: #onset-segments-temp.len()] + } + let nucleus-segments-temp = smart-clusters(syll.nucleus) + if nucleus-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many nucleus segments in syllable #(i + 1) (max 5 to avoid line crossings). Found: #nucleus-segments-temp.len()] + } + } + + // Find stressed syllable (head of foot) + let head-idx = 0 + for (i, syll) in syllables.enumerate() { + if syll.stressed { + head-idx = i + } + } + + let sym_foot = symbol.at(0, default: "Σ") + let sym_syll = symbol.at(1, default: "σ") + + let diagram-scale = scale + + box(baseline: 50%, cetz.canvas(length: 1cm * diagram-scale, { + import cetz.draw: * + set-style(stroke: 0.7 * diagram-scale * 1pt) + + // Distance multiplier lookup (floor: 0.5 for levels 0–1, 1.0 for levels 2+) + let dist-mult(level) = { + let result = 1.0 + if distance != none { + let floor = if level <= 1 { 0.5 } else { 1.0 } + for entry in distance { + if entry.at(0) == level { + result = calc.max(floor, entry.at(1)) + } + } + } + result + } + + let segment-spacing = 0.35 + let min-gap-between-sylls = 0.8 + let default-spacing = 1.6 + + // Calculate extents for each syllable + let syllable-extents = () + for syll in syllables { + let has-onset = syll.onset != "" + let has-coda = syll.coda != "" + let num-onset = if has-onset { smart-clusters(syll.onset).len() } else { 0 } + let num-nucleus = smart-clusters(syll.nucleus).len() + let num-coda = if has-coda { smart-clusters(syll.coda).len() } else { 0 } + let min-gap = 0.75 + + // Calculate constituent positions + let onset-x-rel = if has-onset { + let min-offset = (num-onset - 1) * segment-spacing / 2 + (num-nucleus - 1) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { -min-offset } else { -default-offset } + } else { 0 } + + let coda-x-rel = if has-coda { + let min-offset = (num-nucleus + num-coda - 2) * segment-spacing / 2 + 0.4 + let default-offset = 0.7 + if min-offset > default-offset { min-offset } else { default-offset } + } else { 0 } + + // Calculate segment widths + let onset-width = if has-onset { (num-onset - 1) * segment-spacing } else { 0 } + let nucleus-width = (num-nucleus - 1) * segment-spacing + let coda-width = if has-coda { (num-coda - 1) * segment-spacing } else { 0 } + + // Calculate left and right extents + let left-parts = ( + if has-onset { onset-x-rel - onset-width / 2 } else { 0 }, + -nucleus-width / 2, + if has-coda { coda-x-rel - coda-width / 2 } else { 0 }, + ) + let right-parts = ( + if has-onset { onset-x-rel + onset-width / 2 } else { 0 }, + nucleus-width / 2, + if has-coda { coda-x-rel + coda-width / 2 } else { 0 }, + ) + + let left-extent = calc.min(..left-parts) + let right-extent = calc.max(..right-parts) + + syllable-extents.push((left: left-extent, right: right-extent)) + } + + // Calculate adaptive spacing and positions + let syllable-positions = () + for (i, extent) in syllable-extents.enumerate() { + if i == 0 { + syllable-positions.push(0) + } else { + let prev-right = syllable-extents.at(i - 1).right + let required-spacing = prev-right - extent.left + min-gap-between-sylls + let actual-spacing = calc.max(required-spacing, default-spacing) + let prev-position = syllable-positions.at(i - 1) + syllable-positions.push(prev-position + actual-spacing) + } + } + + // Center the structure + let first-left = syllable-positions.at(0) + syllable-extents.at(0).left + let last-right = syllable-positions.at(-1) + syllable-extents.at(-1).right + let total-width = last-right - first-left + let start-x = -total-width / 2 - first-left + + let foot-x = start-x + syllable-positions.at(head-idx) + + // Vertical level positions + let sigma-y = -2.4 + let sigma-y-label = sigma-y + 0.54 + let base-ft-height = -0.9 + (syllables.len() * 0.3) + let ft-sigma-gap = base-ft-height - sigma-y-label + let ft-height = sigma-y-label + ft-sigma-gap * dist-mult(0) + + // Sub-syllable level positions + let or-y = sigma-y - 0.75 * dist-mult(1) + let n-y = or-y - 0.90 * dist-mult(2) + let terminal-y = n-y - 0.95 * dist-mult(3) + + // Draw Ft node above the head + content((foot-x, ft-height), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#sym_foot]) + + // Detect geminates + // A geminate occurs when the last consonant of a coda matches the first consonant of the next onset + let geminates = () + for i in range(syllables.len() - 1) { + if syllables.at(i).coda != "" and syllables.at(i + 1).onset != "" { + let coda-segments = smart-clusters(syllables.at(i).coda) + let onset-segments = smart-clusters(syllables.at(i + 1).onset) + let last-coda = coda-segments.at(-1) + let first-onset = onset-segments.at(0) + + // Check if they match and are consonants (not vowels) + if last-coda == first-onset and not is-vowel(last-coda) { + let gem-x = start-x + (syllable-positions.at(i) + syllable-positions.at(i + 1)) / 2 + geminates.push((syll-idx: i, gem-x: gem-x, gem-text: last-coda)) + } + } + } + + // Draw syllables + for (i, syll) in syllables.enumerate() { + let x-offset = start-x + syllable-positions.at(i) + + // Syllable node + content((x-offset, sigma-y + 0.54), context text( + size: 12 * diagram-scale * 1pt, + font: phonokit-font.get(), + )[#sym_syll]) + + // Line from Ft to σ + line((foot-x, ft-height - 0.25), (x-offset, sigma-y + 0.8)) + + // Check for geminate + let gem-coda-x = none + let gem-coda-text = none + let gem-onset-x = none + let gem-onset-text = none + for gem in geminates { + if gem.syll-idx == i { + gem-coda-x = gem.gem-x + gem-coda-text = gem.gem-text + } + if gem.syll-idx == i - 1 { + gem-onset-x = gem.gem-x + gem-onset-text = gem.gem-text + } + } + + draw-syllable-structure( + x-offset, + sigma-y, + syll, + terminal-y, + diagram-scale: diagram-scale, + geminate-coda-x: gem-coda-x, + geminate-onset-x: gem-onset-x, + geminate-coda-text: gem-coda-text, + geminate-onset-text: gem-onset-text, + or-y: or-y, + n-y: n-y, + ) + } + + // Draw geminate segments + for gem in geminates { + content( + (gem.gem-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#gem.gem-text], + anchor: "north", + ) + } + })) +} + +// Visualizes word, foot, and syllable levels +// Now accepts IPA-style input like "(k a.'v a).l o" +#let word(input, foot: "R", scale: 1.0, symbol: ("ω", "Σ", "σ"), distance: none) = { + // Convert IPA-style input to Unicode + let converted = convert-prosody-input(input) + + let syllables = () + let feet = () + let current-foot = () + let in-foot = false + let buffer = "" + + let chars = converted.codepoints() + let i = 0 + + while i < chars.len() { + let char = chars.at(i) + + if char == "(" { + in-foot = true + current-foot = () + } else if char == ")" { + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + current-foot.push(syllables.len() - 1) + buffer = "" + } + if current-foot.len() > 0 { + feet.push(current-foot) + } + in-foot = false + } else if char == "." { + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + + if in-foot { + current-foot.push(syllables.len() - 1) + } + + buffer = "" + } + } else { + buffer += char + } + + i += 1 + } + + // Handle remaining buffer + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + + if in-foot { + current-foot.push(syllables.len() - 1) + } + } + + // Check for too many onsets/codas/nucleus in any syllable (limit: 5 to avoid crossing lines) + for (i, syll) in syllables.enumerate() { + let coda-segments-temp = if syll.coda != "" { smart-clusters(syll.coda) } else { () } + if coda-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many coda consonants in syllable #(i + 1) (max 5 to avoid line crossings). Found: #coda-segments-temp.len()] + } + let onset-segments-temp = if syll.onset != "" { smart-clusters(syll.onset) } else { () } + if onset-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many onset consonants in syllable #(i + 1) (max 5 to avoid line crossings). Found: #onset-segments-temp.len()] + } + let nucleus-segments-temp = smart-clusters(syll.nucleus) + if nucleus-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many nucleus segments in syllable #(i + 1) (max 5 to avoid line crossings). Found: #nucleus-segments-temp.len()] + } + } + + // Determine which syllables are in feet + let in-foot-set = () + for foot in feet { + for syll-idx in foot { + in-foot-set.push(syll-idx) + } + } + + let sym_word = symbol.at(0, default: "ω") + let sym_foot = symbol.at(1, default: "Σ") + let sym_syll = symbol.at(2, default: "σ") + + let diagram-scale = scale + + // Draw the structure + box(baseline: 50%, cetz.canvas(length: 1cm * diagram-scale, { + import cetz.draw: * + + set-style(stroke: 0.7 * diagram-scale * 1pt) + + let segment-spacing = 0.35 + let min-gap-between-sylls = 0.8 + let default-spacing = 1.6 + + // Distance multiplier lookup (floor: 0.5 for levels 0–1, 1.0 for levels 2–4) + let dist-mult(level) = { + let result = 1.0 + if distance != none { + let floor = if level <= 1 { 0.5 } else { 1.0 } + for entry in distance { + if entry.at(0) == level { + result = calc.max(floor, entry.at(1)) + } + } + } + result + } + + // Vertical level positions (built top-down from σ) + let sigma-y = -2.4 + let sigma-y-label = sigma-y + 0.54 + let base-gap = 0.96 + let foot-y = sigma-y-label + base-gap * dist-mult(1) + + // Sub-syllable level positions + let or-y = sigma-y - 0.75 * dist-mult(2) + let n-y = or-y - 0.90 * dist-mult(3) + let terminal-y = n-y - 0.95 * dist-mult(4) + + + // Calculate extents for each syllable + let syllable-extents = () + for syll in syllables { + let has-onset = syll.onset != "" + let has-coda = syll.coda != "" + let num-onset = if has-onset { smart-clusters(syll.onset).len() } else { 0 } + let num-nucleus = smart-clusters(syll.nucleus).len() + let num-coda = if has-coda { smart-clusters(syll.coda).len() } else { 0 } + let min-gap = 0.75 + + let onset-x-rel = if has-onset { + let min-offset = (num-onset - 1) * segment-spacing / 2 + (num-nucleus - 1) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { -min-offset } else { -default-offset } + } else { 0 } + + let coda-x-rel = if has-coda { + let min-offset = (num-nucleus + num-coda - 2) * segment-spacing / 2 + 0.4 + let default-offset = 0.7 + if min-offset > default-offset { min-offset } else { default-offset } + } else { 0 } + + let onset-width = if has-onset { (num-onset - 1) * segment-spacing } else { 0 } + let nucleus-width = (num-nucleus - 1) * segment-spacing + let coda-width = if has-coda { (num-coda - 1) * segment-spacing } else { 0 } + + let left-parts = ( + if has-onset { onset-x-rel - onset-width / 2 } else { 0 }, + -nucleus-width / 2, + if has-coda { coda-x-rel - coda-width / 2 } else { 0 }, + ) + let right-parts = ( + if has-onset { onset-x-rel + onset-width / 2 } else { 0 }, + nucleus-width / 2, + if has-coda { coda-x-rel + coda-width / 2 } else { 0 }, + ) + + let left-extent = calc.min(..left-parts) + let right-extent = calc.max(..right-parts) + + syllable-extents.push((left: left-extent, right: right-extent)) + } + + // Calculate adaptive spacing and positions + let syllable-positions = () + for (i, extent) in syllable-extents.enumerate() { + if i == 0 { + syllable-positions.push(0) + } else { + let prev-right = syllable-extents.at(i - 1).right + let required-spacing = prev-right - extent.left + min-gap-between-sylls + let actual-spacing = calc.max(required-spacing, default-spacing) + let prev-position = syllable-positions.at(i - 1) + syllable-positions.push(prev-position + actual-spacing) + } + } + + // Center the structure + let first-left = syllable-positions.at(0) + syllable-extents.at(0).left + let last-right = syllable-positions.at(-1) + syllable-extents.at(-1).right + let total-width = last-right - first-left + let start-x = -total-width / 2 - first-left + + // Determine PWd x-position + let pwd-x = 0 + + if feet.len() > 0 { + let target-foot = if foot == "L" { feet.at(0) } else { feet.at(-1) } + + let head-idx = target-foot.at(0) + for syll-idx in target-foot { + if syllables.at(syll-idx).stressed { + head-idx = syll-idx + } + } + pwd-x = start-x + syllable-positions.at(head-idx) + } else if syllables.len() > 0 { + let target-idx = if foot == "L" { 0 } else { syllables.len() - 1 } + pwd-x = start-x + syllable-positions.at(target-idx) + } + + // Calculate minimum PWd height + let clearance-margin = 0.5 + let min-pwd-height = foot-y + base-gap * 1.5 + + for (i, syll) in syllables.enumerate() { + if i not in in-foot-set { + let syll-x = start-x + syllable-positions.at(i) + + for ft in feet { + let head-idx = ft.at(0) + for syll-idx in ft { + if syllables.at(syll-idx).stressed { + head-idx = syll-idx + } + } + let foot-x = start-x + syllable-positions.at(head-idx) + + let is-between = (pwd-x < foot-x and foot-x < syll-x) or (syll-x < foot-x and foot-x < pwd-x) + + if is-between and calc.abs(syll-x - pwd-x) > 0.01 { + let t = (foot-x - pwd-x) / (syll-x - pwd-x) + let required-height = (1.35 * t - 0.35 + clearance-margin) / (1 - t) + min-pwd-height = calc.max(min-pwd-height, required-height) + } + } + } + } + + let pwd-height = calc.max(min-pwd-height, min-pwd-height * dist-mult(0)) + + content((pwd-x, pwd-height), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#sym_word]) + + // Detect geminates + // A geminate occurs when the last consonant of a coda matches the first consonant of the next onset + let geminates = () + for i in range(syllables.len() - 1) { + if syllables.at(i).coda != "" and syllables.at(i + 1).onset != "" { + let coda-segments = smart-clusters(syllables.at(i).coda) + let onset-segments = smart-clusters(syllables.at(i + 1).onset) + let last-coda = coda-segments.at(-1) + let first-onset = onset-segments.at(0) + + // Check if they match and are consonants (not vowels) + if last-coda == first-onset and not is-vowel(last-coda) { + let gem-x = start-x + (syllable-positions.at(i) + syllable-positions.at(i + 1)) / 2 + geminates.push((syll-idx: i, gem-x: gem-x, gem-text: last-coda)) + } + } + } + + // Draw footless syllables + for (i, syll) in syllables.enumerate() { + if i not in in-foot-set { + let x-offset = start-x + syllable-positions.at(i) + + content((x-offset, sigma-y + 0.54), context text( + size: 12 * diagram-scale * 1pt, + font: phonokit-font.get(), + )[#sym_syll]) + line((pwd-x, pwd-height - 0.3), (x-offset, sigma-y + 0.75)) + + let gem-coda-x = none + let gem-coda-text = none + let gem-onset-x = none + let gem-onset-text = none + for gem in geminates { + if gem.syll-idx == i { + gem-coda-x = gem.gem-x + gem-coda-text = gem.gem-text + } + if gem.syll-idx == i - 1 { + gem-onset-x = gem.gem-x + gem-onset-text = gem.gem-text + } + } + + draw-syllable-structure( + x-offset, + sigma-y, + syll, + terminal-y, + diagram-scale: diagram-scale, + geminate-coda-x: gem-coda-x, + geminate-onset-x: gem-onset-x, + geminate-coda-text: gem-coda-text, + geminate-onset-text: gem-onset-text, + or-y: or-y, + n-y: n-y, + ) + } + } + + // Draw each foot + for foot in feet { + let head-idx = foot.at(0) + for syll-idx in foot { + if syllables.at(syll-idx).stressed { + head-idx = syll-idx + } + } + + let foot-x = start-x + syllable-positions.at(head-idx) + + content((foot-x, foot-y), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#sym_foot]) + line((pwd-x, pwd-height - 0.3), (foot-x, foot-y + 0.25)) + + for syll-idx in foot { + let x-offset = start-x + syllable-positions.at(syll-idx) + let syll = syllables.at(syll-idx) + + content((x-offset, sigma-y + 0.54), context text( + size: 12 * diagram-scale * 1pt, + font: phonokit-font.get(), + )[#sym_syll]) + line((foot-x, foot-y - 0.25), (x-offset, sigma-y + 0.8)) + + let gem-coda-x = none + let gem-coda-text = none + let gem-onset-x = none + let gem-onset-text = none + for gem in geminates { + if gem.syll-idx == syll-idx { + gem-coda-x = gem.gem-x + gem-coda-text = gem.gem-text + } + if gem.syll-idx == syll-idx - 1 { + gem-onset-x = gem.gem-x + gem-onset-text = gem.gem-text + } + } + + draw-syllable-structure( + x-offset, + sigma-y, + syll, + terminal-y, + diagram-scale: diagram-scale, + geminate-coda-x: gem-coda-x, + geminate-onset-x: gem-onset-x, + geminate-coda-text: gem-coda-text, + geminate-onset-text: gem-onset-text, + or-y: or-y, + n-y: n-y, + ) + } + } + + // Draw geminate segments + for gem in geminates { + content( + (gem.gem-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#gem.gem-text], + anchor: "north", + ) + } + })) +} + +// Visualizes foot with moraic structure +// Now accepts IPA-style input like "k a.'v a.l o" +#let foot-mora(input, coda: false, scale: 1.0, symbol: ("Σ", "σ", "μ"), distance: none) = { + // Check for problematic diacritic sequences + let problematic-sequences = ("''", ",,", "\\* \\*", "\\t \\t", "::", "((", "))") + for seq in problematic-sequences { + if input.contains(seq) { + return text(fill: red, weight: "bold")[⚠ Warning: Problematic sequence involving diacritics: "#seq"] + } + } + + // Convert IPA-style input to Unicode + let converted = convert-prosody-input(input) + + // Parse syllables from dotted input + let syllables = () + let buffer = "" + let chars = converted.codepoints() + let i = 0 + + while i < chars.len() { + let char = chars.at(i) + + if char == "." { + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + buffer = "" + } + } else if char == "(" or char == ")" { + // Skip parentheses - they're just delimiters, not segments + } else { + buffer += char + } + i += 1 + } + + // Handle remaining buffer + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + } + + // Check for too many onsets/codas/nucleus in any syllable (limit: 5 to avoid crossing lines) + for (i, syll) in syllables.enumerate() { + let coda-segments-temp = if syll.coda != "" { smart-clusters(syll.coda) } else { () } + if coda-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many coda consonants in syllable #(i + 1) (max 5 to avoid line crossings). Found: #coda-segments-temp.len()] + } + let onset-segments-temp = if syll.onset != "" { smart-clusters(syll.onset) } else { () } + if onset-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many onset consonants in syllable #(i + 1) (max 5 to avoid line crossings). Found: #onset-segments-temp.len()] + } + let nucleus-segments-temp = smart-clusters(syll.nucleus) + if nucleus-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many nucleus segments in syllable #(i + 1) (max 5 to avoid line crossings). Found: #nucleus-segments-temp.len()] + } + } + + // Find stressed syllable (head of foot) + let head-idx = 0 + for (i, syll) in syllables.enumerate() { + if syll.stressed { + head-idx = i + } + } + + let sym_foot = symbol.at(0, default: "Σ") + let sym_syll = symbol.at(1, default: "σ") + let sym_mora = symbol.at(2, default: "μ") + + let diagram-scale = scale + + box(baseline: 50%, cetz.canvas(length: 1cm * diagram-scale, { + import cetz.draw: * + set-style(stroke: 0.7 * diagram-scale * 1pt) + + // Distance multiplier lookup (floor: 0.5 for all mora levels) + let dist-mult(level) = { + let result = 1.0 + if distance != none { + for entry in distance { + if entry.at(0) == level { + result = calc.max(0.5, entry.at(1)) + } + } + } + result + } + + let segment-spacing = 0.35 + let min-gap-between-sylls = 0.8 // Same as foot() to prevent overlap + let default-spacing = 1.6 // Same as foot() to prevent overlap + + // Calculate extents for each syllable + let syllable-extents = () + for syll in syllables { + let has-onset = syll.onset != "" + let has-coda = syll.coda != "" + let num-onset = if has-onset { smart-clusters(syll.onset).len() } else { 0 } + let num-nucleus = smart-clusters(syll.nucleus).len() + let num-coda = if has-coda { smart-clusters(syll.coda).len() } else { 0 } + let min-gap = 0.75 + + // Calculate constituent positions (simplified like word()) + // Use segment-based extents without mora adjustments for uniform spacing + let onset-x-rel = if has-onset { + let min-offset = (num-onset - 1) * segment-spacing / 2 + (num-nucleus - 1) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { -min-offset } else { -default-offset } + } else { 0 } + + let coda-x-rel = if has-coda { + let min-offset = (num-nucleus + num-coda - 2) * segment-spacing / 2 + 0.4 + let default-offset = 0.7 + if min-offset > default-offset { min-offset } else { default-offset } + } else { 0 } + + // Calculate segment widths + let onset-width = if has-onset { (num-onset - 1) * segment-spacing } else { 0 } + let nucleus-width = (num-nucleus - 1) * segment-spacing + let coda-width = if has-coda { (num-coda - 1) * segment-spacing } else { 0 } + + // Calculate left and right extents (same as word()) + let left-parts = ( + if has-onset { onset-x-rel - onset-width / 2 } else { 0 }, + -nucleus-width / 2, + if has-coda { coda-x-rel - coda-width / 2 } else { 0 }, + ) + let right-parts = ( + if has-onset { onset-x-rel + onset-width / 2 } else { 0 }, + nucleus-width / 2, + if has-coda { coda-x-rel + coda-width / 2 } else { 0 }, + ) + + let left-extent = calc.min(..left-parts) + let right-extent = calc.max(..right-parts) + + syllable-extents.push((left: left-extent, right: right-extent)) + } + + // Calculate spacing (same as regular foot() for uniform segment spacing) + let syllable-positions = () + for (i, extent) in syllable-extents.enumerate() { + if i == 0 { + syllable-positions.push(0) + } else { + let prev-right = syllable-extents.at(i - 1).right + let required-spacing = prev-right - extent.left + min-gap-between-sylls + let actual-spacing = calc.max(required-spacing, default-spacing) + let prev-position = syllable-positions.at(i - 1) + syllable-positions.push(prev-position + actual-spacing) + } + } + + // Center the structure + let first-left = syllable-positions.at(0) + syllable-extents.at(0).left + let last-right = syllable-positions.at(-1) + syllable-extents.at(-1).right + let total-width = last-right - first-left + let start-x = -total-width / 2 - first-left + + let foot-x = start-x + syllable-positions.at(head-idx) + + // Vertical level positions + let sigma-y = -2.4 + let sigma-y-label = sigma-y + 0.54 + let base-ft-height = -0.9 + (syllables.len() * 0.3) + let ft-sigma-gap = base-ft-height - sigma-y-label + let ft-height = sigma-y-label + ft-sigma-gap * dist-mult(0) + + // Mora level positions (σ→μ = level 1, μ→segments = level 2) + let mora-base-gap = 1.57 + let mora-y = sigma-y-label - mora-base-gap * 1.2 * dist-mult(1) + let terminal-y = mora-y - mora-base-gap * dist-mult(2) + + // Draw Ft node above the head + content((foot-x, ft-height), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#sym_foot]) + + // Detect geminates + // A geminate occurs when the last consonant of a coda matches the first consonant of the next onset + let geminates = () + for i in range(syllables.len() - 1) { + if syllables.at(i).coda != "" and syllables.at(i + 1).onset != "" { + let coda-segments = smart-clusters(syllables.at(i).coda) + let onset-segments = smart-clusters(syllables.at(i + 1).onset) + let last-coda = coda-segments.at(-1) + let first-onset = onset-segments.at(0) + + // Check if they match and are consonants (not vowels) + if last-coda == first-onset and not is-vowel(last-coda) { + let gem-x = start-x + (syllable-positions.at(i) + syllable-positions.at(i + 1)) / 2 + geminates.push((syll-idx: i, gem-x: gem-x, gem-text: last-coda)) + } + } + } + + // Draw syllables with moraic structure + for (i, syll) in syllables.enumerate() { + let x-offset = start-x + syllable-positions.at(i) + + // Syllable node + content((x-offset, sigma-y + 0.54), context text( + size: 12 * diagram-scale * 1pt, + font: phonokit-font.get(), + )[#sym_syll]) + + // Line from Ft to σ + line((foot-x, ft-height - 0.25), (x-offset, sigma-y + 0.8)) + + // Check for geminate + let gem-coda-x = none + let gem-onset-x = none + for gem in geminates { + if gem.syll-idx == i { + gem-coda-x = gem.gem-x + } + if gem.syll-idx == i - 1 { + gem-onset-x = gem.gem-x + } + } + + draw-moraic-structure( + x-offset, + sigma-y, + syll, + terminal-y, + coda: coda, + diagram-scale: diagram-scale, + geminate-coda-x: gem-coda-x, + geminate-onset-x: gem-onset-x, + mora-symbol: sym_mora, + mora-y-override: mora-y, + ) + } + + // Draw geminate segments + for gem in geminates { + content( + (gem.gem-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#gem.gem-text], + anchor: "north", + ) + } + })) +} + +// Visualizes word with moraic structure +// Now accepts IPA-style input like "(k a.'v a).l o" +#let word-mora(input, foot: "R", coda: false, scale: 1.0, symbol: ("ω", "Σ", "σ", "μ"), distance: none) = { + // Check for problematic diacritic sequences + let problematic-sequences = ("''", ",,", "\\* \\*", "\\t \\t", "::", "((", "))") + for seq in problematic-sequences { + if input.contains(seq) { + return text(fill: red, weight: "bold")[⚠ Warning: Problematic sequence involving diacritics: "#seq"] + } + } + + // Convert IPA-style input to Unicode + let converted = convert-prosody-input(input) + + let syllables = () + let feet = () + let current-foot = () + let in-foot = false + let buffer = "" + + let chars = converted.codepoints() + let i = 0 + + while i < chars.len() { + let char = chars.at(i) + + if char == "(" { + in-foot = true + current-foot = () + } else if char == ")" { + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + current-foot.push(syllables.len() - 1) + buffer = "" + } + if current-foot.len() > 0 { + feet.push(current-foot) + } + in-foot = false + } else if char == "." { + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + + if in-foot { + current-foot.push(syllables.len() - 1) + } + + buffer = "" + } + } else { + buffer += char + } + + i += 1 + } + + // Handle remaining buffer + if buffer != "" { + let clean-buffer = if buffer.starts-with("'") { buffer.slice(1) } else { buffer } + let parsed = parse-syllable(clean-buffer) + syllables.push(( + onset: parsed.onset, + nucleus: parsed.nucleus, + coda: parsed.coda, + stressed: buffer.starts-with("'"), + )) + + if in-foot { + current-foot.push(syllables.len() - 1) + } + } + + // Check for too many onsets/codas/nucleus in any syllable (limit: 5 to avoid crossing lines) + for (i, syll) in syllables.enumerate() { + let coda-segments-temp = if syll.coda != "" { smart-clusters(syll.coda) } else { () } + if coda-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many coda consonants in syllable #(i + 1) (max 5 to avoid line crossings). Found: #coda-segments-temp.len()] + } + let onset-segments-temp = if syll.onset != "" { smart-clusters(syll.onset) } else { () } + if onset-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many onset consonants in syllable #(i + 1) (max 5 to avoid line crossings). Found: #onset-segments-temp.len()] + } + let nucleus-segments-temp = smart-clusters(syll.nucleus) + if nucleus-segments-temp.len() > 5 { + return text( + fill: red, + weight: "bold", + )[⚠ Warning: Too many nucleus segments in syllable #(i + 1) (max 5 to avoid line crossings). Found: #nucleus-segments-temp.len()] + } + } + + // Determine which syllables are in feet + let in-foot-set = () + for foot in feet { + for syll-idx in foot { + in-foot-set.push(syll-idx) + } + } + + let sym_word = symbol.at(0, default: "ω") + let sym_foot = symbol.at(1, default: "Σ") + let sym_syll = symbol.at(2, default: "σ") + let sym_mora = symbol.at(3, default: "μ") + + let diagram-scale = scale + + // Draw the structure + box(baseline: 50%, cetz.canvas(length: 1cm * diagram-scale, { + import cetz.draw: * + + set-style(stroke: 0.7 * diagram-scale * 1pt) + + // Distance multiplier lookup (floor: 0.5 for all mora levels) + let dist-mult(level) = { + let result = 1.0 + if distance != none { + for entry in distance { + if entry.at(0) == level { + result = calc.max(0.5, entry.at(1)) + } + } + } + result + } + + let segment-spacing = 0.35 + let min-gap-between-sylls = 0.6 + let default-spacing = 1.4 + + // Calculate extents for each syllable + let syllable-extents = () + for syll in syllables { + let has-onset = syll.onset != "" + let has-coda = syll.coda != "" + let num-onset = if has-onset { smart-clusters(syll.onset).len() } else { 0 } + let num-nucleus = smart-clusters(syll.nucleus).len() + let num-coda = if has-coda { smart-clusters(syll.coda).len() } else { 0 } + + // Calculate constituent positions (simplified like word()) + // Use segment-based extents without mora adjustments for uniform spacing + let min-gap = 0.75 + + let onset-x-rel = if has-onset { + let min-offset = (num-onset - 1) * segment-spacing / 2 + (num-nucleus - 1) * segment-spacing / 2 + min-gap + let default-offset = 0.7 + if min-offset > default-offset { -min-offset } else { -default-offset } + } else { 0 } + + let coda-x-rel = if has-coda { + let min-offset = (num-nucleus + num-coda - 2) * segment-spacing / 2 + 0.4 + let default-offset = 0.7 + if min-offset > default-offset { min-offset } else { default-offset } + } else { 0 } + + // Calculate segment widths + let onset-width = if has-onset { (num-onset - 1) * segment-spacing } else { 0 } + let nucleus-width = (num-nucleus - 1) * segment-spacing + let coda-width = if has-coda { (num-coda - 1) * segment-spacing } else { 0 } + + // Calculate left and right extents (same as word()) + let left-parts = ( + if has-onset { onset-x-rel - onset-width / 2 } else { 0 }, + -nucleus-width / 2, + if has-coda { coda-x-rel - coda-width / 2 } else { 0 }, + ) + let right-parts = ( + if has-onset { onset-x-rel + onset-width / 2 } else { 0 }, + nucleus-width / 2, + if has-coda { coda-x-rel + coda-width / 2 } else { 0 }, + ) + + let left-extent = calc.min(..left-parts) + let right-extent = calc.max(..right-parts) + + syllable-extents.push((left: left-extent, right: right-extent)) + } + + // Calculate spacing based on uniform segment-to-segment distance + let syllable-positions = () + for (i, extent) in syllable-extents.enumerate() { + if i == 0 { + syllable-positions.push(0) + } else { + // Use same spacing calculation as word() for uniform segment spacing + let prev-right = syllable-extents.at(i - 1).right + let required-spacing = prev-right - extent.left + min-gap-between-sylls + let actual-spacing = calc.max(required-spacing, default-spacing) + let prev-position = syllable-positions.at(i - 1) + syllable-positions.push(prev-position + actual-spacing) + } + } + + // Center the structure + let first-left = syllable-positions.at(0) + syllable-extents.at(0).left + let last-right = syllable-positions.at(-1) + syllable-extents.at(-1).right + let total-width = last-right - first-left + let start-x = -total-width / 2 - first-left + + // Determine PWd x-position + let pwd-x = 0 + + if feet.len() > 0 { + let target-foot = if foot == "L" { feet.at(0) } else { feet.at(-1) } + let target-syll-idx = target-foot.at(0) + for (i, syll) in target-foot.enumerate() { + let this-syll = syllables.at(syll) + if this-syll.stressed { + target-syll-idx = syll + break + } + } + pwd-x = start-x + syllable-positions.at(target-syll-idx) + } + + // Vertical level positions + let sigma-y = -2.4 + let sigma-y-label = sigma-y + 0.54 + let base-gap = 0.96 + let ft-y = sigma-y-label + base-gap * dist-mult(1) + + // Mora level positions (σ→μ = level 2, μ→segments = level 3) + let mora-base-gap = 1.57 + let mora-y = sigma-y-label - mora-base-gap * 1.2 * dist-mult(2) + let terminal-y = mora-y - mora-base-gap * dist-mult(3) + + // Calculate minimum PWd height + let clearance-margin = 0.5 + let min-pwd-height = ft-y + base-gap * 1.5 + + // Check geometric constraints for unfooted syllables + for (i, syll) in syllables.enumerate() { + if i not in in-foot-set { + let syll-x = start-x + syllable-positions.at(i) + + // Check all feet to find those between PWd and this footless syllable + for ft in feet { + // Find foot's head position + let head-idx = ft.at(0) + for syll-idx in ft { + if syllables.at(syll-idx).stressed { + head-idx = syll-idx + } + } + let foot-x = start-x + syllable-positions.at(head-idx) + + // Check if foot is between PWd and footless syllable + let is-between = (pwd-x < foot-x and foot-x < syll-x) or (syll-x < foot-x and foot-x < pwd-x) + + if is-between and calc.abs(syll-x - pwd-x) > 0.01 { + let t = (foot-x - pwd-x) / (syll-x - pwd-x) + let required-height = (1.35 * t - 0.35 + clearance-margin) / (1 - t) + min-pwd-height = calc.max(min-pwd-height, required-height) + } + } + } + } + + let pwd-y = calc.max(min-pwd-height, min-pwd-height * dist-mult(0)) + + // Draw PWd node + content((pwd-x, pwd-y), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#sym_word]) + + // Detect geminates + // A geminate occurs when the last consonant of a coda matches the first consonant of the next onset + let geminates = () + for i in range(syllables.len() - 1) { + if syllables.at(i).coda != "" and syllables.at(i + 1).onset != "" { + let coda-segments = smart-clusters(syllables.at(i).coda) + let onset-segments = smart-clusters(syllables.at(i + 1).onset) + let last-coda = coda-segments.at(-1) + let first-onset = onset-segments.at(0) + + // Check if they match and are consonants (not vowels) + if last-coda == first-onset and not is-vowel(last-coda) { + let gem-x = start-x + (syllable-positions.at(i) + syllable-positions.at(i + 1)) / 2 + geminates.push((syll-idx: i, gem-x: gem-x, gem-text: last-coda)) + } + } + } + + // Draw feet + for (foot-idx, foot-sylls) in feet.enumerate() { + // Find head of foot (stressed syllable) + let head-idx = foot-sylls.at(0) + for syll-idx in foot-sylls { + if syllables.at(syll-idx).stressed { + head-idx = syll-idx + break + } + } + + let foot-x = start-x + syllable-positions.at(head-idx) + content((foot-x, ft-y), context text(size: 12 * diagram-scale * 1pt, font: phonokit-font.get())[#sym_foot]) + line((pwd-x, pwd-y - 0.3), (foot-x, ft-y + 0.25)) + + // Draw lines from Ft to syllables in this foot + for syll-idx in foot-sylls { + let syll-x = start-x + syllable-positions.at(syll-idx) + line((foot-x, ft-y - 0.25), (syll-x, sigma-y + 0.8)) + } + } + + // Draw lines from PWd to unfooted syllables + for (i, syll) in syllables.enumerate() { + if i not in in-foot-set { + let syll-x = start-x + syllable-positions.at(i) + line((pwd-x, pwd-y - 0.3), (syll-x, sigma-y + 0.75)) + } + } + + // Draw syllables with moraic structure + for (i, syll) in syllables.enumerate() { + let x-offset = start-x + syllable-positions.at(i) + + // Syllable node + content((x-offset, sigma-y + 0.54), context text( + size: 12 * diagram-scale * 1pt, + font: phonokit-font.get(), + )[#sym_syll]) + + // Check for geminate + let gem-coda-x = none + let gem-onset-x = none + for gem in geminates { + if gem.syll-idx == i { + gem-coda-x = gem.gem-x + } + if gem.syll-idx == i - 1 { + gem-onset-x = gem.gem-x + } + } + + draw-moraic-structure( + x-offset, + sigma-y, + syll, + terminal-y, + coda: coda, + diagram-scale: diagram-scale, + geminate-coda-x: gem-coda-x, + geminate-onset-x: gem-onset-x, + mora-symbol: sym_mora, + mora-y-override: mora-y, + ) + } + + // Draw geminate segments + for gem in geminates { + content( + (gem.gem-x, terminal-y), + context text(size: 11 * diagram-scale * 1pt, font: phonokit-font.get())[#gem.gem-text], + anchor: "north", + ) + } + })) +} diff --git a/packages/preview/phonokit/0.5.11/sonority.typ b/packages/preview/phonokit/0.5.11/sonority.typ new file mode 100644 index 0000000000..34d0962269 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/sonority.typ @@ -0,0 +1,242 @@ +// Sonority Module +// Visualize sonority profiles of phonemic transcriptions +// Based on Parker (2011) sonority scale + +#import "ipa.typ": ipa, ipa-to-unicode +#import "_config.typ": phonokit-font +#import "@preview/cetz:0.5.2" + +// Sonority scale based on Parker (2011) +#let sonority-scale = ( + "a": 13, + "ɑ": 13, + "æ": 13, + "ɐ": 13, + "ɛ": 12, + "ɔ": 12, + "ʌ": 12, + "œ": 12, + "e": 12, + "o": 12, + "ə": 12, + "ɘ": 12, + "ɵ": 12, + "ø": 12, + "ɚ": 12, + "ɝ": 12, + "i": 12, + "u": 12, + "ɪ": 12, + "ʊ": 12, + "y": 12, + "ɯ": 12, + "ɨ": 12, + "ʉ": 12, + "j": 11, + "w": 11, + "ɥ": 11, + "ɰ": 11, + "ɾ": 10, + "ɽ": 10, + "l": 9, + "ɫ": 9, + "ʎ": 9, + "ʟ": 9, + "ɬ": 8, + "ɮ": 8, + "r": 8, + "ʀ": 8, + "ɹ": 8, + "ʁ": 8, + "ɻ": 8, + "m": 7, + "n": 7, + "ŋ": 7, + "ɲ": 7, + "ɳ": 7, + "ɴ": 7, + "ɱ": 7, + "v": 6, + "z": 6, + "ʒ": 6, + "ð": 6, + "ʐ": 6, + "ʝ": 6, + "ɣ": 6, + "β": 6, + "f": 3, + "s": 3, + "ʃ": 3, + "θ": 3, + "x": 3, + "χ": 3, + "ħ": 3, + "h": 3, + "ɸ": 3, + "ç": 3, + "ʂ": 3, + "b": 4, + "d": 4, + "g": 4, + "ɡ": 4, + "ɢ": 4, + "ɖ": 4, + "ʄ": 4, + "ɗ": 4, + "ɓ": 4, + "p": 1, + "t": 1, + "k": 1, + "q": 1, + "ʔ": 1, + "ʈ": 1, + "c": 1, + "d͡ʒ": 5, + "d͡z": 5, + "d͡ʑ": 5, + "ɖ͡ʐ": 5, + "t͡ʃ": 2, + "t͡s": 2, + "t͡ɕ": 2, + "ʈ͡ʂ": 2, + "t͡θ": 2, + "p͡f": 2, +) + +#let get-sonority(phoneme) = { + if phoneme in sonority-scale { + sonority-scale.at(phoneme) + } else { + let base = phoneme.codepoints().at(0) + if base in sonority-scale { + sonority-scale.at(base) + } else { + 5 + } + } +} + +// Parse IPA string into individual phonemes and syllable boundaries +#let parse-phonemes(ipa-string) = { + let cleaned = ipa-string.replace("ˈ", "").replace("ˌ", "").replace("ː", "") + let syllable-boundaries = () + let phonemes = () + let position = 0 + let basic-clusters = cleaned.clusters() + let i = 0 + + while i < basic-clusters.len() { + let cluster = basic-clusters.at(i) + if cluster == "." { + syllable-boundaries.push(position) + i += 1 + } else if cluster == " " or cluster == "-" { + i += 1 + } else if cluster.contains("͡") { + if i + 1 < basic-clusters.len() { + let next = basic-clusters.at(i + 1) + if next != " " and next != "-" and next != "." { + phonemes.push(cluster + next) + i += 2 + position += 1 + } else { + phonemes.push(cluster) + i += 1 + position += 1 + } + } else { + phonemes.push(cluster) + i += 1 + position += 1 + } + } else { + phonemes.push(cluster) + i += 1 + position += 1 + } + } + (phonemes, syllable-boundaries) +} + +// Main sonority plotting function +#let sonority( + word, // Tipa-style string + syl: none, // (Legacy/unused directly in calculation now, kept for API compatibility) + stressed: none, // Index of stressed syllable + box-size: 0.8, // Size of phoneme boxes + scale: 1.0, // Overall scale + y-range: (0, 8), // Sonority range for y-axis + show-lines: true, // Connect phonemes with lines +) = { + // Convert tipa-style input to IPA + let ipa-string = ipa-to-unicode(word) + let (phonemes, syllable-boundaries) = parse-phonemes(ipa-string) + + // Truncation check + let original-count = phonemes.len() + let truncated = original-count > 10 + if truncated { + phonemes = phonemes.slice(0, 10) + syllable-boundaries = syllable-boundaries.filter(pos => pos <= 10) + } + + let sonority-values = phonemes.map(p => get-sonority(p)) + let n-phonemes = phonemes.len() + let width = n-phonemes * 1.5 + let height = 3 + + if truncated { + text(size: 9pt, fill: red, weight: "bold")[⚠ Warning: Truncated to first 10 phonemes.] + v(0.5em) + } + + cetz.canvas(length: scale * 1cm, { + import cetz.draw: * + set-origin((0, 0)) + + // Draw connecting lines first + if show-lines and n-phonemes > 1 { + for i in range(n-phonemes - 1) { + let x1 = float(i) * 1.5 + let y1 = float((sonority-values.at(i) - y-range.at(0))) / float((y-range.at(1) - y-range.at(0))) * float(height) + let x2 = float(i + 1) * 1.5 + let y2 = ( + float((sonority-values.at(i + 1) - y-range.at(0))) / float((y-range.at(1) - y-range.at(0))) * float(height) + ) + line((x1, y1), (x2, y2), stroke: (thickness: 0.5pt, paint: gray, dash: "dashed")) + } + } + + // Draw phoneme boxes with syllable-based alternating colors + for (i, phoneme) in phonemes.enumerate() { + let sonority = sonority-values.at(i) + let x = float(i) * 1.5 + let y = float((sonority - y-range.at(0))) / float((y-range.at(1) - y-range.at(0))) * float(height) + + // Determine syllable index by checking how many boundaries we have passed + let syllable-index = syllable-boundaries.filter(b => b <= i).len() + + // Alternate colors: Even syllables = White, Odd syllables = Gray + let box-fill = if calc.even(syllable-index) { + white + } else { + rgb("dddddd") // Light gray + } + + // Draw box + rect( + (x - box-size / 2, y - box-size / 2), + (x + box-size / 2, y + box-size / 2), + fill: box-fill, + stroke: 0.5pt + black, + ) + + // Add phoneme label (always black now) + content( + (x, y), + context text(size: 10pt, font: phonokit-font.get(), fill: black)[#phoneme], + anchor: "center", + ) + } + }) +} diff --git a/packages/preview/phonokit/0.5.11/sound-shift.typ b/packages/preview/phonokit/0.5.11/sound-shift.typ new file mode 100644 index 0000000000..a1e6229631 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/sound-shift.typ @@ -0,0 +1,261 @@ +#import "@preview/cetz:0.5.2" +#import "ipa.typ": ipa-to-unicode +#import "_config.typ": phonokit-font + +#let _ss-render-label(label) = { + if type(label) == str { + ipa-to-unicode(label) + } else { + label + } +} + +#let _ss-node-id(node) = { + if "id" in node { + str(node.id) + } else if "label" in node and type(node.label) == str { + node.label + } else { + panic("Each sound-shift node must have either an id or a string label.") + } +} + +#let _ss-node-label(node) = { + if "label" in node { + node.label + } else if "id" in node { + node.id + } else { + panic("Each sound-shift node must have either a label or an id.") + } +} + +#let _ss-node-pos(node) = { + if "at" in node { + node.at + } else if "pos" in node { + node.pos + } else { + panic("Each sound-shift node must define a position with at: (x, y).") + } +} + +#let _ss-arrow-end(endpoint, nodes) = { + if type(endpoint) == str { + let key = str(endpoint) + let node = nodes.find(n => n.id == key) + assert(node != none, message: "Unknown sound-shift endpoint: " + key) + (pos: node.at, radius: node.radius) + } else if type(endpoint) == array and endpoint.len() >= 2 { + let radius = endpoint.at(2, default: 0) + (pos: (endpoint.at(0), endpoint.at(1)), radius: radius) + } else { + panic("Arrow endpoints must be node ids or coordinate pairs.") + } +} + +#let _ss-arrow-dict(arrow) = { + if type(arrow) == dictionary { + arrow + } else if type(arrow) == array and arrow.len() >= 2 { + (from: arrow.at(0), to: arrow.at(1)) + } else { + panic("Each sound-shift arrow must be a dictionary or a (from, to) tuple.") + } +} + +#let sound-shift( + nodes: (), + arrows: (), + highlights: (), + node-size: 2.2em, + text-fill: black, + highlight-fill: luma(230), + highlight-radius: 0.42, + arrow-color: black, + arrow-style: "solid", + arrow-width: 0.8pt, + arrow-size: 1.0, + curved: false, + curve: 0.45, + scale: 1.0, +) = { + assert(type(nodes) == array, message: "nodes must be an array") + assert(nodes.len() > 0, message: "nodes array cannot be empty") + + let scale-factor = scale + let normalized-nodes = nodes.map(node => { + assert(type(node) == dictionary, message: "Each sound-shift node must be a dictionary.") + let id = _ss-node-id(node) + let label = _ss-node-label(node) + let pos = _ss-node-pos(node) + let normalized = ( + id: id, + label: label, + at: pos, + fill: node.at("fill", default: none), + size: node.at("size", default: node-size), + text-fill: node.at("text-fill", default: text-fill), + radius: node.at("radius", default: highlight-radius), + ) + normalized + }) + + let all-points = normalized-nodes.map(n => n.at) + let arrow-points = arrows.map(arrow => { + let spec = _ss-arrow-dict(arrow) + let from = _ss-arrow-end(spec.from, normalized-nodes) + let to = _ss-arrow-end(spec.to, normalized-nodes) + (from.pos, to.pos) + }) + + let pad = 0.6 + let xs = all-points.map(p => p.at(0)) + arrow-points.map(pair => pair.at(0).at(0)) + arrow-points.map(pair => pair.at(1).at(0)) + let ys = all-points.map(p => p.at(1)) + arrow-points.map(pair => pair.at(0).at(1)) + arrow-points.map(pair => pair.at(1).at(1)) + let min-x = calc.min(..xs) - pad + let max-x = calc.max(..xs) + pad + let min-y = calc.min(..ys) - pad + let max-y = calc.max(..ys) + pad + + box(inset: 1.2em, baseline: 40%, cetz.canvas(length: scale-factor * 1cm, { + import cetz.draw: * + + for item in highlights { + let pos = _ss-arrow-end(item, normalized-nodes).pos + circle( + pos, + radius: highlight-radius, + fill: highlight-fill, + stroke: none, + ) + } + + for node in normalized-nodes { + if node.fill != none { + circle( + node.at, + radius: node.radius, + fill: node.fill, + stroke: none, + ) + } + } + + for arrow in arrows { + let spec = _ss-arrow-dict(arrow) + let from-end = _ss-arrow-end(spec.from, normalized-nodes) + let to-end = _ss-arrow-end(spec.to, normalized-nodes) + let from-pos = from-end.pos + let to-pos = to-end.pos + let dx = to-pos.at(0) - from-pos.at(0) + let dy = to-pos.at(1) - from-pos.at(1) + let dist = calc.sqrt(dx * dx + dy * dy) + let safe-dist = if dist == 0 { 1 } else { dist } + let ux = dx / safe-dist + let uy = dy / safe-dist + let start-gap = spec.at("start-gap", default: from-end.radius) + let end-gap = spec.at("end-gap", default: to-end.radius) + let drawn-from = ( + from-pos.at(0) + ux * start-gap, + from-pos.at(1) + uy * start-gap, + ) + let drawn-to = ( + to-pos.at(0) - ux * end-gap, + to-pos.at(1) - uy * end-gap, + ) + let px = -dy / safe-dist + let py = dx / safe-dist + let amount = spec.at("curve", default: curve) + let ctrl = spec.at("ctrl", default: ( + (from-pos.at(0) + to-pos.at(0)) / 2 + px * safe-dist * amount, + (from-pos.at(1) + to-pos.at(1)) / 2 + py * safe-dist * amount, + )) + let local-curved = spec.at("curved", default: curved) + let local-color = spec.at("color", default: arrow-color) + let local-width = spec.at("width", default: arrow-width) * scale-factor + let local-style = spec.at("style", default: arrow-style) + let start-tangent = if local-curved { + let sx = ctrl.at(0) - from-pos.at(0) + let sy = ctrl.at(1) - from-pos.at(1) + let sd = calc.sqrt(sx * sx + sy * sy) + if sd == 0 { (ux, uy) } else { (sx / sd, sy / sd) } + } else { + (ux, uy) + } + let end-tangent = if local-curved { + let ex = to-pos.at(0) - ctrl.at(0) + let ey = to-pos.at(1) - ctrl.at(1) + let ed = calc.sqrt(ex * ex + ey * ey) + if ed == 0 { (ux, uy) } else { (ex / ed, ey / ed) } + } else { + (ux, uy) + } + let drawn-from = ( + from-pos.at(0) + start-tangent.at(0) * start-gap, + from-pos.at(1) + start-tangent.at(1) * start-gap, + ) + let drawn-to = ( + to-pos.at(0) - end-tangent.at(0) * end-gap, + to-pos.at(1) - end-tangent.at(1) * end-gap, + ) + let drawn-ctrl = if local-curved { + let from-shift = ( + drawn-from.at(0) - from-pos.at(0), + drawn-from.at(1) - from-pos.at(1), + ) + let to-shift = ( + drawn-to.at(0) - to-pos.at(0), + drawn-to.at(1) - to-pos.at(1), + ) + ( + ctrl.at(0) + (from-shift.at(0) + to-shift.at(0)) / 2, + ctrl.at(1) + (from-shift.at(1) + to-shift.at(1)) / 2, + ) + } else { + ctrl + } + let shaft-stroke = if local-style == "dashed" { + (paint: local-color, thickness: local-width, dash: "dashed") + } else if local-style == "dotted" { + (paint: local-color, thickness: local-width, dash: "dotted") + } else { + (paint: local-color, thickness: local-width) + } + let head-stroke = (paint: local-color, thickness: local-width) + let mark-style = (end: ">", fill: local-color, scale: spec.at("arrow-size", default: arrow-size) * scale-factor) + + if local-style == "dashed" or local-style == "dotted" { + if local-curved { + bezier(drawn-from, drawn-to, drawn-ctrl, stroke: shaft-stroke) + } else { + line(drawn-from, drawn-to, stroke: shaft-stroke) + } + let tiny = 0.01 + let head-anchor = ( + drawn-to.at(0) - end-tangent.at(0) * tiny, + drawn-to.at(1) - end-tangent.at(1) * tiny, + ) + line(head-anchor, drawn-to, stroke: head-stroke, mark: mark-style) + } else if local-curved { + bezier(drawn-from, drawn-to, drawn-ctrl, stroke: shaft-stroke, mark: mark-style) + } else { + line(drawn-from, drawn-to, stroke: shaft-stroke, mark: mark-style) + } + } + + for node in normalized-nodes { + content( + node.at, + anchor: "center", + context text( + font: phonokit-font.get(), + size: node.size * scale-factor, + fill: node.text-fill, + top-edge: "x-height", + bottom-edge: "baseline", + _ss-render-label(node.label), + ), + ) + } + })) +} diff --git a/packages/preview/phonokit/0.5.11/typst.toml b/packages/preview/phonokit/0.5.11/typst.toml new file mode 100644 index 0000000000..6890db6997 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/typst.toml @@ -0,0 +1,23 @@ +[package] +categories = ["utility", "text", "visualization"] +disciplines = ["linguistics"] +name = "phonokit" +version = "0.5.11" +exclude = ["gallery/"] +keywords = [ + "linguistics", + "phonology", + "phonetics", + "IPA", + "transcription", + "prosody", + "optimality-theory", + "features", + "autosegmental", +] +entrypoint = "lib.typ" +authors = ["Guilherme D. Garcia "] +license = "MIT" +description = "A toolkit to create phonological representations" +repository = "https://github.com/guilhermegarcia/phonokit" +homepage = "https://gdgarcia.ca/phonokit" diff --git a/packages/preview/phonokit/0.5.11/ui-lang.typ b/packages/preview/phonokit/0.5.11/ui-lang.typ new file mode 100644 index 0000000000..9a93953708 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/ui-lang.typ @@ -0,0 +1,369 @@ +// Shared UI-language helpers for translatable labels. + +#let _ui-lang-aliases = ( + "en": "en", + "english": "en", + "fr": "fr", + "french": "fr", + "pt": "pt", + "portuguese": "pt", +) + +#let _ui-lang-available = "en, english, fr, french, pt, portuguese" + +#let resolve-ui-lang(ui-lang) = { + if type(ui-lang) != str { + none + } else { + let key = lower(ui-lang.trim()) + _ui-lang-aliases.at(key, default: none) + } +} + +#let ui-lang-error(ui-lang) = [*Error:* UI language "#ui-lang" not available. \ + Available UI languages: #_ui-lang-available] + +#let _consonant-ui-labels = ( + "en": ( + places: ( + "Bilabial", + "Labiodental", + "Dental", + "Alveolar", + "Postalveolar", + "Retroflex", + "Palatal", + "Velar", + "Uvular", + "Pharyngeal", + "Glottal", + ), + places_short: ( + "Bilab", + "Labdent", + "Dent", + "Alv", + "Postalv", + "Retro", + "Pal", + "Vel", + "Uvu", + "Phar", + "Glot", + ), + manners: ( + "Plosive", + "Nasal", + "Trill", + "Tap or Flap", + "Fricative", + "Lateral fricative", + "Approximant", + "Lateral approximant", + ), + manners_short: ( + "Plos", + "Nas", + "Trill", + "Tap/Flap", + "Fric", + "Lat fric", + "Approx", + "Lat approx", + ), + aspirated_plosive: "Plosive (aspirated)", + aspirated_plosive_short: "Plos (asp)", + affricate: "Affricate", + affricate_short: "Affr", + aspirated_affricate: "Affricate (aspirated)", + aspirated_affricate_short: "Affr (asp)", + ), + "fr": ( + places: ( + "Bilabiale", + "Labio-dentale", + "Dentale", + "Alvéolaire", + "Post-alvéolaire", + "Retroflexe", + "Palatale", + "Vélaire", + "Uvulaire", + "Pharyngale", + "Glottale", + ), + places_short: ( + "Bilab", + "Labiod", + "Dent", + "Alv", + "Postalv", + "Retrof", + "Pal", + "Vel", + "Uvul", + "Phar", + "Glot", + ), + manners: ( + "Plosive", + "Nasale", + "Vibrante", + "Battue", + "Fricative", + "Fricative latérale", + "Approximante", + "Approximante latérale", + ), + manners_short: ( + "Plos", + "Nas", + "Vibr", + "Batt", + "Fric", + "Fric lat", + "Approx", + "Approx lat", + ), + aspirated_plosive: "Plosive (aspirée)", + aspirated_plosive_short: "Plos (asp)", + affricate: "Affriquée", + affricate_short: "Affr", + aspirated_affricate: "Affriquée (aspirée)", + aspirated_affricate_short: "Affr (asp)", + ), + "pt": ( + places: ( + "Bilabial", + "Labiodental", + "Dental", + "Alveolar", + "Pós-alveolar", + "Retroflexa", + "Palatal", + "Velar", + "Uvular", + "Faríngea", + "Glotal", + ), + places_short: ( + "Bilab", + "Labdent", + "Dent", + "Alv", + "Posalv", + "Retrof", + "Pal", + "Vel", + "Uvul", + "Far", + "Glot", + ), + manners: ( + "Plosiva", + "Nasal", + "Vibrante", + "Tepe", + "Fricativa", + "Fricativa lateral", + "Aproximante", + "Aproximante lateral", + ), + manners_short: ( + "Plos", + "Nas", + "Vibr", + "Tepe", + "Fric", + "Fric lat", + "Aprox", + "Aprox lat", + ), + aspirated_plosive: "Plosiva (aspirada)", + aspirated_plosive_short: "Plos (asp)", + affricate: "Africada", + affricate_short: "Afr", + aspirated_affricate: "Africada (aspirada)", + aspirated_affricate_short: "Afr (asp)", + ), +) + +#let ui-consonant-labels(ui-lang) = _consonant-ui-labels.at(ui-lang) + +#let _feature-ui-labels = ( + "en": ( + "consonantal": "consonantal", + "sonorant": "sonorant", + "continuant": "continuant", + "delayed_release": "del rel", + "approximant": "approximant", + "tap": "tap", + "trill": "trill", + "nasal": "nasal", + "voice": "voice", + "spread_gl": "spread gl", + "constr_gl": "constr gl", + "labial": "labial", + "round": "round", + "labiodental": "labiodental", + "coronal": "coronal", + "anterior": "anterior", + "distributed": "distributed", + "strident": "strident", + "lateral": "lateral", + "dorsal": "dorsal", + "high": "high", + "low": "low", + "front": "front", + "back": "back", + "tense": "tense", + ), + "fr": ( + "consonantal": "consonantique", + "sonorant": "sonant", + "continuant": "continu", + "delayed_release": "rel ret", + "approximant": "approximant", + "tap": "battu", + "trill": "vibrant", + "nasal": "nasal", + "voice": "voisé", + "spread_gl": "gl écartée", + "constr_gl": "gl contr", + "labial": "labial", + "round": "arrondi", + "labiodental": "labio-dental", + "coronal": "coronal", + "anterior": "antérieur", + "distributed": "distribué", + "strident": "strident", + "lateral": "latéral", + "dorsal": "dorsal", + "high": "haut", + "low": "bas", + "front": "antérieur", + "back": "postérieur", + "tense": "tendu", + ), + "pt": ( + "consonantal": "consonantal", + "sonorant": "sonorante", + "continuant": "contínuo", + "delayed_release": "lib ret", + "approximant": "aproximante", + "tap": "tepe", + "trill": "vibrante", + "nasal": "nasal", + "voice": "vozeado", + "spread_gl": "gl aberta", + "constr_gl": "gl constr", + "labial": "labial", + "round": "arredondado", + "labiodental": "labiodental", + "coronal": "coronal", + "anterior": "anterior", + "distributed": "distribuído", + "strident": "estridente", + "lateral": "lateral", + "dorsal": "dorsal", + "high": "alto", + "low": "baixo", + "front": "anterior", + "back": "posterior", + "tense": "tenso", + ), +) + +#let ui-feature-label(key, ui-lang) = _feature-ui-labels.at(ui-lang).at(key, default: key) + +#let _geom-ui-labels = ( + "en": ( + "root": "root", + "laryngeal": "laryngeal", + "oral cavity": "oral cavity", + "C-place": "C-place", + "vocalic": "vocalic", + "V-place": "V-place", + "aperture": "aperture", + "constricted": "constr", + "continuant": "cont", + "distributed": "distr", + "dorsal": "dor", + "coronal": "cor", + "labial": "lab", + "anterior": "ant", + "radical": "rad", + "high": "hi", + "low": "low", + "back": "back", + "round": "round", + "tense": "tense", + "voice": "voice", + "nasal": "nasal", + "lateral": "lat", + "spread": "spread", + "open1": "open1", + "open2": "open2", + "open3": "open3", + ), + "fr": ( + "root": "racine", + "laryngeal": "laryng", + "oral cavity": "cav orale", + "C-place": "lieu C", + "vocalic": "vocaliq", + "V-place": "lieu V", + "aperture": "apert", + "constricted": "constr", + "continuant": "cont", + "distributed": "distr", + "dorsal": "dor", + "coronal": "cor", + "labial": "lab", + "anterior": "ant", + "radical": "rad", + "high": "haut", + "low": "bas", + "back": "post", + "round": "arr", + "tense": "tens", + "voice": "vois", + "nasal": "nas", + "lateral": "lat", + "spread": "écart", + "open1": "ouv1", + "open2": "ouv2", + "open3": "ouv3", + ), + "pt": ( + "root": "raiz", + "laryngeal": "laring", + "oral cavity": "cav oral", + "C-place": "ponto C", + "vocalic": "vocal", + "V-place": "ponto V", + "aperture": "abert", + "constricted": "constr", + "continuant": "cont", + "distributed": "distr", + "dorsal": "dor", + "coronal": "cor", + "labial": "lab", + "anterior": "ant", + "radical": "rad", + "high": "alto", + "low": "baixo", + "back": "post", + "round": "arr", + "tense": "tens", + "voice": "voz", + "nasal": "nas", + "lateral": "lat", + "spread": "abert", + "open1": "abr1", + "open2": "abr2", + "open3": "abr3", + ), +) + +#let ui-geom-label(key, ui-lang) = _geom-ui-labels.at(ui-lang).at(key, default: key) diff --git a/packages/preview/phonokit/0.5.11/vowels.typ b/packages/preview/phonokit/0.5.11/vowels.typ new file mode 100644 index 0000000000..64212c9bf4 --- /dev/null +++ b/packages/preview/phonokit/0.5.11/vowels.typ @@ -0,0 +1,546 @@ +#import "@preview/cetz:0.5.2": canvas, draw +#import "ipa.typ": ipa-to-unicode +#import "_config.typ": phonokit-font + +// Vowel data with relative positions (0-1 scale) +// frontness: 0 = front, 0.5 = central, 1 = back +// height: 1 = close, 0.67 = close-mid, 0.33 = open-mid, 0 = open +// rounded: affects horizontal positioning within minimal pairs +#let vowel-data = ( + "i": (frontness: 0.05, height: 1.00, rounded: false), + "y": (frontness: 0.05, height: 1.00, rounded: true), + "ɨ": (frontness: 0.50, height: 1.00, rounded: false), + "ʉ": (frontness: 0.50, height: 1.00, rounded: true), + "ɯ": (frontness: 0.95, height: 1.00, rounded: false), + "u": (frontness: 0.95, height: 1.00, rounded: true), + "ɪ": (frontness: 0.15, height: 0.85, rounded: false), + "ʏ": (frontness: 0.25, height: 0.85, rounded: true), + "ʊ": (frontness: 0.85, height: 0.85, rounded: true), + "e": (frontness: 0.05, height: 0.67, rounded: false), + "ø": (frontness: 0.05, height: 0.67, rounded: true), + "ɘ": (frontness: 0.50, height: 0.67, rounded: false), + "ɵ": (frontness: 0.50, height: 0.67, rounded: true), + "ɤ": (frontness: 0.95, height: 0.67, rounded: false), + "o": (frontness: 0.95, height: 0.67, rounded: true), + "ə": (frontness: 0.585, height: 0.51, rounded: false), + "ɛ": (frontness: 0.05, height: 0.34, rounded: false), + "œ": (frontness: 0.05, height: 0.34, rounded: true), + "ɜ": (frontness: 0.50, height: 0.34, rounded: false), + "ɞ": (frontness: 0.50, height: 0.34, rounded: true), + "ʌ": (frontness: 0.95, height: 0.34, rounded: false), + "ɔ": (frontness: 0.95, height: 0.34, rounded: true), + "æ": (frontness: 0.05, height: 0.15, rounded: false), + "ɐ": (frontness: 0.585, height: 0.18, rounded: false), + "a": (frontness: 0.05, height: 0.00, rounded: false), + "ɶ": (frontness: 0.05, height: 0.00, rounded: true), + "ɑ": (frontness: 0.95, height: 0.00, rounded: false), + "ɒ": (frontness: 0.95, height: 0.00, rounded: true), +) + +// Calculate actual position from relative coordinates +#let get-vowel-position(vowel-info, trapezoid, width, height, offset) = { + let front = vowel-info.frontness + let h = vowel-info.height + + // Calculate y coordinate + let y = -height / 2 + (h * height) + + // Interpolate x based on trapezoid shape at this height + let t = 1 - h // interpolation factor (0 at top, 1 at bottom) + let left-x = trapezoid.at(0).at(0) * (1 - t) + trapezoid.at(3).at(0) * t + let right-x = trapezoid.at(1).at(0) * (1 - t) + trapezoid.at(2).at(0) * t + + let x = 0 + + // Front vowels (frontness < 0.4) + if front < 0.4 { + // Extreme front (< 0.15): tense vowels like i, e + if front < 0.15 { + if vowel-info.rounded { + // Front rounded: inside (right of left edge) + x = left-x + offset + } else { + // Front unrounded: outside (left of left edge) + x = left-x - offset + } + } // Near-front (0.15-0.4): lax vowels like ɪ, ɛ + // Always positioned inside the trapezoid + else { + x = left-x + (front * (right-x - left-x)) + } + } // Back vowels (frontness > 0.6) + else if front > 0.6 { + // Extreme back (> 0.85): tense vowels like u, o + if front > 0.85 { + if vowel-info.rounded { + // Back rounded: outside (right of right edge) + x = right-x + offset + } else { + // Back unrounded: inside (left of right edge) + x = right-x - offset + } + } // Near-back (0.6-0.85): lax vowels like ʊ + // Always positioned inside the trapezoid + else { + x = left-x + (front * (right-x - left-x)) + } + } // Central vowels + else { + // Calculate base central position + let center-x = left-x + (front * (right-x - left-x)) + + // Apply full offset for rounded/unrounded pairs (same as front/back) + if vowel-info.rounded { + x = center-x + offset // Rounded to the right + } else { + x = center-x - offset // Unrounded to the left + } + } + + (x, y) +} + +// Language vowel inventories +#let language-vowels = ( + "spanish": "aeoiu", + "portuguese": "iɔeaouɛ", + "italian": "iɔeaouɛ", + "english": "iɪaeɛæɑɔoʊuʌə", + "french": "iœɑɔøeaouɛyə", + "german": "iyʊuɪʏeøoɔɐaɛœ", + "japanese": "ieaou", + "russian": "iɨueoa", + "arabic": "aiu", + "all": "iyɨʉɯuɪʏʊeøɘɵɤoəɛœɜɞʌɔæɐaɶɑɒ", + // Add more languages here or adjust existing inventories +) + +#let language-nasal-vowels = ( + "french": ("ɛ", "œ", "ɔ", "ɑ"), +) + +#let _strip-nasal(vowel) = vowel.normalize(form: "nfd").replace("̃", "") + +#let _collect-custom-vowels(input) = { + let converted = ipa-to-unicode(input) + let oral = "" + let nasals = () + + for cluster in converted.clusters() { + let decomposed = cluster.normalize(form: "nfd") + let base = _strip-nasal(decomposed) + if base in vowel-data { + if base not in oral { + oral += base + } + if "̃" in decomposed and base not in nasals { + nasals.push(base) + } + } + } + + (oral: oral, nasals: nasals) +} + +// Main vowels function +#let vowels( + ..args, // Optional positional vowel-string (read below) — allows `lang`-only calls + lang: none, + width: 8, + height: 6, + rows: 3, // Only 2 internal horizontal lines + cols: 2, // Only 1 vertical line inside trapezoid + scale: 0.7, // Scale factor for entire chart + nasals: false, // Draw nasalized copies of plotted vowels + arrows: (), // List of (from-tipa-str, to-tipa-str) tuples + arrow-color: black, // Color for arrow lines and heads + arrow-style: "solid", // "solid" or "dashed" + curved: false, // Curve arrows with a quadratic bezier arc + shift: (), // List of (tipa-str, x-offset, y-offset) tuples + shift-color: gray, // Color for shifted vowel symbols + shift-size: none, // Font size for shifted vowels; none = same as regular + highlight: (), // List of tipa strings whose background circle is highlighted + highlight-color: luma(220), // Circle color for highlighted vowels (default: light gray) +) = { + // Read the optional positional argument: vowel symbols, a language name, or + // tipa-style IPA. It is optional (via the `..args` sink) so that `lang`-only + // calls like `vowels(lang: "spanish")` work — a parameter with a default value + // would be named-only in Typst and could not be passed positionally. + assert(args.pos().len() <= 1, + message: "vowels: expected at most one positional argument (the vowel string)") + assert(args.named().len() == 0, + message: "vowels: unexpected named argument(s): " + args.named().keys().join(", ")) + let vowel-string = args.pos().at(0, default: none) + + // Determine which vowels to plot + let vowels-to-plot = "" + let nasal-target-vowels = () + let error-msg = none + + // Check if vowel-string is actually a language name + if vowel-string != none and vowel-string in language-vowels { + // It's a language name - use language vowels + vowels-to-plot = language-vowels.at(vowel-string) + } else if lang != none { + // Explicit lang parameter provided + if lang in language-vowels { + vowels-to-plot = language-vowels.at(lang) + } else { + // Language not available - prepare error message + let available = language-vowels.keys().join(", ") + error-msg = [*Error:* Language "#lang" not available. \ Available languages: #available] + } + } else if vowel-string != none and vowel-string != "" { + // Use as manual vowel specification - keep oral bases for plotting and + // remember which vowels were explicitly nasalized in the input. + let parsed = _collect-custom-vowels(vowel-string) + vowels-to-plot = parsed.oral + nasal-target-vowels = parsed.nasals + } else { + // Nothing specified + error-msg = [*Error:* Either provide vowel string or language name] + } + + // If there's an error, display it and return + if error-msg != none { + return error-msg + } + + // Calculate scaled dimensions + let scaled-width = width * scale + let scaled-height = height * scale + let scaled-offset = 0.55 * scale + let scaled-circle-radius = 0.35 * scale + let scaled-bullet-radius = 0.09 * scale + let scaled-font-size = 22 * scale + let scaled-line-thickness = 0.85 * scale + let scaled-arrow-mark = 1.5 * scale + let scaled-nasal-dy = 0.44 * scale + let scaled-nasal-font-size = scaled-font-size * 0.9 * 1pt + let nasal-color = gray.darken(10%) + let resolved-shift-size = if shift-size != none { shift-size * scale } else { scaled-font-size * 1pt } + // Split highlight into regular-vowel highlights (strings) and shifted-vowel + // highlights (arrays in the same (tipa-str, x, y) format as shift:) + let highlight-set = highlight.filter(h => type(h) == str).map(ipa-to-unicode) + let highlight-shifts = highlight.filter(h => type(h) != str) + .map(h => (ipa-to-unicode(h.at(0)), h.at(1), h.at(2))) + + canvas({ + import draw: * + + // Define the trapezoidal quadrilateral using scaled dimensions + let trapezoid = ( + (-scaled-width / 2, scaled-height / 2.), + (scaled-width / 2., scaled-height / 2), + (scaled-width / 2., -scaled-height / 2), + (-scaled-width / 10, -scaled-height / 2), + ) + + // Draw horizontal grid lines + for i in range(1, rows) { + let t = i / rows + let left-x = trapezoid.at(0).at(0) * (1 - t) + trapezoid.at(3).at(0) * t + let right-x = trapezoid.at(1).at(0) * (1 - t) + trapezoid.at(2).at(0) * t + let y = scaled-height / 2 - (scaled-height * t) + + line((left-x, y), (right-x, y), stroke: (paint: gray.lighten(30%), thickness: scaled-line-thickness * 1pt)) + } + + // Draw vertical grid lines + for i in range(1, cols) { + let t = i / cols + let top-x = trapezoid.at(0).at(0) * (1 - t) + trapezoid.at(1).at(0) * t + let bottom-x = trapezoid.at(3).at(0) * (1 - t) + trapezoid.at(2).at(0) * t + + line((top-x, scaled-height / 2), (bottom-x, -scaled-height / 2), stroke: ( + paint: gray.lighten(30%), + thickness: scaled-line-thickness * 1pt, + )) + } + + // Draw the outline + line(..trapezoid, close: true, stroke: (paint: gray.lighten(30%), thickness: scaled-line-thickness * 1pt)) + + // Resolve an arrow endpoint to a canvas position. + // endpoint is either a tipa string (canonical vowel position) or a + // (tipa-str, x-offset, y-offset) array (shifted position, same format as shift:). + // Returns (found, position) where found is false if the vowel is unknown. + let pos-of(endpoint) = { + let is-str = type(endpoint) == str + let v = ipa-to-unicode(if is-str { endpoint } else { endpoint.at(0) }) + let x-off = if is-str { 0 } else { endpoint.at(1) } + let y-off = if is-str { 0 } else { endpoint.at(2) } + if v in vowel-data { + let base = get-vowel-position(vowel-data.at(v), trapezoid, scaled-width, scaled-height, scaled-offset) + (true, (base.at(0) + x-off, base.at(1) + y-off)) + } else { + (false, (0, 0)) + } + } + + // Collect vowel positions + let vowel-positions = () + for vowel in vowels-to-plot.clusters() { + if vowel in vowel-data { + let vowel-info = vowel-data.at(vowel) + let pos = get-vowel-position(vowel-info, trapezoid, scaled-width, scaled-height, scaled-offset) + vowel-positions.push((vowel: vowel, info: vowel-info, pos: pos)) + } + } + + // Build the global obstacle list for curved-arrow avoidance: + // all plotted vowels plus all shifted copies, pre-computed once. + let shifted-obs = shift + .filter(s => ipa-to-unicode(s.at(0)) in vowel-data) + .map(s => { + let sv = ipa-to-unicode(s.at(0)) + let base = get-vowel-position(vowel-data.at(sv), trapezoid, scaled-width, scaled-height, scaled-offset) + (base.at(0) + s.at(1), base.at(1) + s.at(2)) + }) + let preset-name = if vowel-string != none and vowel-string in language-vowels { + vowel-string + } else { + lang + } + let nasal-bases = if preset-name != none and preset-name in language-nasal-vowels { + language-nasal-vowels.at(preset-name) + } else { + nasal-target-vowels + } + let nasal-positions = if nasals { + vowel-positions + .filter(vp => vp.vowel in nasal-bases) + .map(vp => ( + vowel: vp.vowel, + pos: (vp.pos.at(0), vp.pos.at(1) + scaled-nasal-dy), + label: vp.vowel + "̃", + )) + } else { + () + } + let all-obstacle-positions = vowel-positions.map(vp => vp.pos) + shifted-obs + nasal-positions.map(np => np.pos) + + // True if p is either at/near endpoint ep, or is its minimal-pair partner. + // Minimal-pair partners share height (dy ≈ 0) and lie exactly 2×scaled-offset + // apart in x (one rounded, one unrounded). They are geometrically inseparable + // from their partner, so arrows approaching ep will inevitably pass near the + // partner and should not try to avoid it. The ±40% relative tolerance on the + // distance keeps the check scale-independent while excluding near-front lax + // pairs like ɪ/ʏ (whose inter-vowel distance is only ~68% of 2×scaled-offset). + let near-or-pair(p, ep) = { + let dx = p.at(0) - ep.at(0) + let dy = p.at(1) - ep.at(1) + let d = calc.sqrt(dx*dx + dy*dy) + let at-endpoint = d < scaled-circle-radius * 0.5 + let is-pair = calc.abs(dy) < scaled-offset * 0.1 and calc.abs(d - 2*scaled-offset) < scaled-offset * 0.4 + at-endpoint or is-pair + } + + // Draw arrows in three phases so that arrowhead clustering can be applied + // after all control points and tangents are known. + + // ── Phase 1: compute drawing parameters for every valid arrow ──────────── + let arrows-data = () + for arrow in arrows { + let fr = pos-of(arrow.at(0)) + let tr = pos-of(arrow.at(1)) + if fr.at(0) and tr.at(0) { + let from-pos = fr.at(1) + let to-pos = tr.at(1) + let dx = to-pos.at(0) - from-pos.at(0) + let dy = to-pos.at(1) - from-pos.at(1) + let dist = calc.sqrt(dx * dx + dy * dy) + let mid-x = (from-pos.at(0) + to-pos.at(0)) / 2 + let mid-y = (from-pos.at(1) + to-pos.at(1)) / 2 + + // Control point selection for curved arrows. + // Two obstacle lists are used in priority order: + // strict – excludes only the exact endpoints; pair partners are live + // obstacles so the algorithm avoids departing through them + // (e.g. ɔ→ɪ must not start by crossing through ʌ). + // loose – also excludes pair partners; used as a fallback only when + // no strict-safe path exists. + // Sampling at t = 0.1 and 0.9 (in addition to interior midpoints) catches + // obstacles very close to the source or destination vowel. + let ctrl = if curved { + let px = -dy / dist // CCW perpendicular unit vector + let py = dx / dist + let ccw-sm = (mid-x + px * dist * 0.30, mid-y + py * dist * 0.30) + let cw-sm = (mid-x - px * dist * 0.30, mid-y - py * dist * 0.30) + let ccw-lg = (mid-x + px * dist * 0.55, mid-y + py * dist * 0.55) + let cw-lg = (mid-x - px * dist * 0.55, mid-y - py * dist * 0.55) + let local-obs-strict = all-obstacle-positions.filter(p => { + let dfx = p.at(0) - from-pos.at(0) + let dfy = p.at(1) - from-pos.at(1) + let dtx = p.at(0) - to-pos.at(0) + let dty = p.at(1) - to-pos.at(1) + let far-from = calc.sqrt(dfx*dfx + dfy*dfy) > scaled-circle-radius * 0.5 + let far-to = calc.sqrt(dtx*dtx + dty*dty) > scaled-circle-radius * 0.5 + far-from and far-to + }) + let local-obs-loose = all-obstacle-positions.filter(p => + not near-or-pair(p, from-pos) and not near-or-pair(p, to-pos) + ) + let clearance = scaled-circle-radius * 1.3 + let sample-ts = (0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9) + let hits(obs, c) = obs.any(ob => + sample-ts.any(t => { + let bx = (1-t)*(1-t)*from-pos.at(0) + 2*t*(1-t)*c.at(0) + t*t*to-pos.at(0) + let by = (1-t)*(1-t)*from-pos.at(1) + 2*t*(1-t)*c.at(1) + t*t*to-pos.at(1) + let ex = ob.at(0) - bx + let ey = ob.at(1) - by + calc.sqrt(ex*ex + ey*ey) < clearance + }) + ) + let ctrl-candidates = (ccw-sm, cw-sm, ccw-lg, cw-lg) + let chosen = ctrl-candidates.find(c => not hits(local-obs-strict, c)) + let chosen = if chosen != none { chosen } else { + ctrl-candidates.find(c => not hits(local-obs-loose, c)) + } + if chosen != none { chosen } else { ccw-sm } + } else { + (mid-x + (-dy / dist) * dist * 0.3, mid-y + (dx / dist) * dist * 0.3) + } + + // Tangent at destination: ctrl→to-pos for curves, chord for straight lines. + let tangent = if curved { + let ex = to-pos.at(0) - ctrl.at(0) + let ey = to-pos.at(1) - ctrl.at(1) + let ed = calc.sqrt(ex * ex + ey * ey) + (ex / ed, ey / ed) + } else { + (dx / dist, dy / dist) + } + + // Pull endpoint back to circle edge along the arrival tangent + let adjusted-to = ( + to-pos.at(0) - tangent.at(0) * scaled-circle-radius, + to-pos.at(1) - tangent.at(1) * scaled-circle-radius, + ) + + arrows-data.push(( + from-pos: from-pos, + to-pos: to-pos, + ctrl: ctrl, + tangent: tangent, + adjusted-to: adjusted-to, + )) + } + } + + // ── Phase 2: merge arrowheads converging at the same vowel ─────────────── + // When multiple arrows target the same vowel and their adjusted-to points + // are within cluster-radius of each other, snap them all to their centroid + // and use the averaged (renormalized) tangent for a consistent arrowhead. + let cluster-radius = scaled-circle-radius + let arrows-data = arrows-data.map(a => { + let cluster = arrows-data.filter(b => { + let dtx = b.to-pos.at(0) - a.to-pos.at(0) + let dty = b.to-pos.at(1) - a.to-pos.at(1) + let same-target = calc.sqrt(dtx*dtx + dty*dty) < 0.01 + let dax = b.adjusted-to.at(0) - a.adjusted-to.at(0) + let day = b.adjusted-to.at(1) - a.adjusted-to.at(1) + same-target and calc.sqrt(dax*dax + day*day) < cluster-radius + }) + if cluster.len() > 1 { + let n = cluster.len() + let tx = cluster.map(b => b.tangent.at(0)).sum() / n + let ty = cluster.map(b => b.tangent.at(1)).sum() / n + let tn = calc.sqrt(tx*tx + ty*ty) + let avg-tan = if tn > 0.001 { (tx/tn, ty/tn) } else { a.tangent } + // Re-derive adjusted-to from the normalised tangent so the tip lands + // exactly on the circle edge (the centroid of circle-edge points sits + // strictly inside the circle and would leave the head floating there). + let snapped = ( + a.to-pos.at(0) - avg-tan.at(0) * scaled-circle-radius, + a.to-pos.at(1) - avg-tan.at(1) * scaled-circle-radius, + ) + (from-pos: a.from-pos, to-pos: a.to-pos, ctrl: a.ctrl, + tangent: avg-tan, adjusted-to: snapped) + } else { + a + } + }) + + // ── Phase 3: render ────────────────────────────────────────────────────── + let shaft-stroke = (paint: arrow-color, thickness: scaled-line-thickness * 1.5pt, + dash: if arrow-style == "dashed" { "dashed" } else { none }) + let head-stroke = (paint: arrow-color, thickness: scaled-line-thickness * 1.5pt) + let mark-style = (end: ">", fill: arrow-color, scale: scaled-arrow-mark) + for a in arrows-data { + let from-pos = a.from-pos + let adjusted-to = a.adjusted-to + let ctrl = a.ctrl + let tangent = a.tangent + if arrow-style == "dashed" { + // Draw dashed shaft without a mark + if curved { + bezier(from-pos, adjusted-to, ctrl, stroke: shaft-stroke) + } else { + line(from-pos, adjusted-to, stroke: shaft-stroke) + } + // Solid near-zero segment at the tip renders the mark independently of + // the dash pattern, correctly oriented along the arrival tangent + let tiny = 0.01 + let head-anchor = ( + adjusted-to.at(0) - tangent.at(0) * tiny, + adjusted-to.at(1) - tangent.at(1) * tiny, + ) + line(head-anchor, adjusted-to, stroke: head-stroke, mark: mark-style) + } else { + // Solid: shaft and arrowhead in one draw call + if curved { + bezier(from-pos, adjusted-to, ctrl, stroke: shaft-stroke, mark: mark-style) + } else { + line(from-pos, adjusted-to, stroke: shaft-stroke, mark: mark-style) + } + } + } + + // Draw bullets between minimal pairs (same frontness/height, different rounding) + for i in range(vowel-positions.len()) { + for j in range(i + 1, vowel-positions.len()) { + let v1 = vowel-positions.at(i) + let v2 = vowel-positions.at(j) + + // Check if they form a minimal pair + let same-front = v1.info.frontness == v2.info.frontness + let same-height = v1.info.height == v2.info.height + let diff-round = v1.info.rounded != v2.info.rounded + + if same-front and same-height and diff-round { + // Draw bullet at midpoint between vowels + let mid-x = (v1.pos.at(0) + v2.pos.at(0)) / 2 + let mid-y = (v1.pos.at(1) + v2.pos.at(1)) / 2 + circle((mid-x, mid-y), radius: scaled-bullet-radius, fill: black) + } + } + } + + // Plot vowels with background circles (white, or highlight color if highlighted) + for vp in vowel-positions { + let circle-fill = if vp.vowel in highlight-set { highlight-color } else { white } + circle(vp.pos, radius: scaled-circle-radius, fill: circle-fill, stroke: none) + content(vp.pos, context text(size: scaled-font-size * 1pt, font: phonokit-font.get(), top-edge: "x-height", bottom-edge: "baseline", vp.vowel)) + } + + // Draw schematic nasalized copies slightly offset from the oral vowels. + for np in nasal-positions { + content(np.pos, context text(size: scaled-nasal-font-size, font: phonokit-font.get(), fill: nasal-color, top-edge: "x-height", bottom-edge: "baseline", np.label)) + } + + // Draw shifted vowels (on top of regular vowels) + for s in shift { + let vowel = ipa-to-unicode(s.at(0)) + let x-off = s.at(1) + let y-off = s.at(2) + if vowel in vowel-data { + let base-pos = get-vowel-position(vowel-data.at(vowel), trapezoid, scaled-width, scaled-height, scaled-offset) + let shifted-pos = (base-pos.at(0) + x-off, base-pos.at(1) + y-off) + let shift-fill = if highlight-shifts.any(h => h.at(0) == vowel and h.at(1) == x-off and h.at(2) == y-off) { highlight-color } else { white } + circle(shifted-pos, radius: scaled-circle-radius, fill: shift-fill, stroke: none) + content(shifted-pos, context text(size: resolved-shift-size, font: phonokit-font.get(), fill: shift-color, top-edge: "x-height", bottom-edge: "baseline", vowel)) + } + } + }) +}