From 68e6d4ce4a06243bbe47e7fcc3b097b3a2884d48 Mon Sep 17 00:00:00 2001 From: "Guilherme D. Garcia" Date: Tue, 6 Oct 2026 18:40:50 -0400 Subject: [PATCH 1/3] phonokit:0.5.19 --- packages/preview/phonokit/0.5.19/LICENSE | 21 + packages/preview/phonokit/0.5.19/README.md | 192 ++ packages/preview/phonokit/0.5.19/_config.typ | 8 + .../preview/phonokit/0.5.19/autosegmental.typ | 275 ++ .../preview/phonokit/0.5.19/consonants.typ | 979 ++++++++ packages/preview/phonokit/0.5.19/ex.typ | 408 +++ packages/preview/phonokit/0.5.19/extras.typ | 39 + packages/preview/phonokit/0.5.19/features.typ | 2140 ++++++++++++++++ .../0.5.19/gallery/autoseg_example_1.png | Bin 0 -> 5355 bytes .../0.5.19/gallery/autoseg_example_1.typ | 11 + .../0.5.19/gallery/autoseg_example_2.png | Bin 0 -> 11274 bytes .../0.5.19/gallery/autoseg_example_2.typ | 22 + .../0.5.19/gallery/autoseg_example_3.png | Bin 0 -> 57914 bytes .../0.5.19/gallery/autoseg_example_3.typ | 47 + .../0.5.19/gallery/consonants_example.png | Bin 0 -> 104958 bytes .../0.5.19/gallery/consonants_example.typ | 8 + .../phonokit/0.5.19/gallery/feat_geom.png | Bin 0 -> 45066 bytes .../phonokit/0.5.19/gallery/feat_geom.typ | 9 + .../0.5.19/gallery/features_example.png | Bin 0 -> 127361 bytes .../0.5.19/gallery/features_example.typ | 5 + .../phonokit/0.5.19/gallery/grid_example.png | Bin 0 -> 5387 bytes .../phonokit/0.5.19/gallery/grid_example.typ | 4 + .../phonokit/0.5.19/gallery/ipa_example.png | Bin 0 -> 28220 bytes .../phonokit/0.5.19/gallery/ipa_example.typ | 17 + .../0.5.19/gallery/maxent_example.png | Bin 0 -> 131582 bytes .../0.5.19/gallery/maxent_example.typ | 41 + .../0.5.19/gallery/multi-tier_example.png | Bin 0 -> 17166 bytes .../0.5.19/gallery/multi-tier_example.typ | 18 + .../phonokit/0.5.19/gallery/ot_example.png | Bin 0 -> 22397 bytes .../phonokit/0.5.19/gallery/ot_example.typ | 17 + .../0.5.19/gallery/syllable_example.png | Bin 0 -> 8740 bytes .../0.5.19/gallery/syllable_example.typ | 6 + .../0.5.19/gallery/vowels_example.png | Bin 0 -> 24371 bytes .../0.5.19/gallery/vowels_example.typ | 17 + .../phonokit/0.5.19/gallery/word_example.png | Bin 0 -> 22647 bytes .../phonokit/0.5.19/gallery/word_example.typ | 5 + packages/preview/phonokit/0.5.19/geom.typ | 2028 +++++++++++++++ packages/preview/phonokit/0.5.19/grids.typ | 114 + packages/preview/phonokit/0.5.19/hasse.typ | 444 ++++ .../preview/phonokit/0.5.19/intonational.typ | 80 + packages/preview/phonokit/0.5.19/ipa.typ | 422 ++++ packages/preview/phonokit/0.5.19/lib.typ | 1170 +++++++++ .../preview/phonokit/0.5.19/multi-tier.typ | 510 ++++ packages/preview/phonokit/0.5.19/ot.typ | 1262 ++++++++++ .../preview/phonokit/0.5.19/phonetics.typ | 1017 ++++++++ packages/preview/phonokit/0.5.19/prosody.typ | 2212 +++++++++++++++++ packages/preview/phonokit/0.5.19/sonority.typ | 247 ++ .../preview/phonokit/0.5.19/sound-shift.typ | 261 ++ packages/preview/phonokit/0.5.19/typst.toml | 23 + packages/preview/phonokit/0.5.19/ui-lang.typ | 369 +++ packages/preview/phonokit/0.5.19/vowels.typ | 605 +++++ 51 files changed, 15053 insertions(+) create mode 100644 packages/preview/phonokit/0.5.19/LICENSE create mode 100644 packages/preview/phonokit/0.5.19/README.md create mode 100644 packages/preview/phonokit/0.5.19/_config.typ create mode 100644 packages/preview/phonokit/0.5.19/autosegmental.typ create mode 100644 packages/preview/phonokit/0.5.19/consonants.typ create mode 100644 packages/preview/phonokit/0.5.19/ex.typ create mode 100644 packages/preview/phonokit/0.5.19/extras.typ create mode 100644 packages/preview/phonokit/0.5.19/features.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/autoseg_example_1.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/autoseg_example_1.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/autoseg_example_2.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/autoseg_example_2.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/autoseg_example_3.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/autoseg_example_3.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/consonants_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/consonants_example.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/feat_geom.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/feat_geom.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/features_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/features_example.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/grid_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/grid_example.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/ipa_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/ipa_example.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/maxent_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/maxent_example.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/multi-tier_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/multi-tier_example.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/ot_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/ot_example.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/syllable_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/syllable_example.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/vowels_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/vowels_example.typ create mode 100644 packages/preview/phonokit/0.5.19/gallery/word_example.png create mode 100644 packages/preview/phonokit/0.5.19/gallery/word_example.typ create mode 100644 packages/preview/phonokit/0.5.19/geom.typ create mode 100644 packages/preview/phonokit/0.5.19/grids.typ create mode 100644 packages/preview/phonokit/0.5.19/hasse.typ create mode 100644 packages/preview/phonokit/0.5.19/intonational.typ create mode 100644 packages/preview/phonokit/0.5.19/ipa.typ create mode 100644 packages/preview/phonokit/0.5.19/lib.typ create mode 100644 packages/preview/phonokit/0.5.19/multi-tier.typ create mode 100644 packages/preview/phonokit/0.5.19/ot.typ create mode 100644 packages/preview/phonokit/0.5.19/phonetics.typ create mode 100644 packages/preview/phonokit/0.5.19/prosody.typ create mode 100644 packages/preview/phonokit/0.5.19/sonority.typ create mode 100644 packages/preview/phonokit/0.5.19/sound-shift.typ create mode 100644 packages/preview/phonokit/0.5.19/typst.toml create mode 100644 packages/preview/phonokit/0.5.19/ui-lang.typ create mode 100644 packages/preview/phonokit/0.5.19/vowels.typ diff --git a/packages/preview/phonokit/0.5.19/LICENSE b/packages/preview/phonokit/0.5.19/LICENSE new file mode 100644 index 0000000000..e116f4b73a --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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.19/README.md b/packages/preview/phonokit/0.5.19/README.md new file mode 100644 index 0000000000..eadc5c822f --- /dev/null +++ b/packages/preview/phonokit/0.5.19/README.md @@ -0,0 +1,192 @@ +
+ + + + 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) + +`vowels()` and `consonants()` check built-in preset names before interpreting +custom IPA inventories. Empty or whitespace-only input (or no argument) renders +nothing while the user is getting started, unless a `lang:` is supplied. For +example, `vowels("english")` uses a preset, while +`vowels("aeiou")` plots a custom inventory. An unknown name such as +`vowels("indonesian")` or `consonants("indonesian")` displays an error instead of a chart when it +contains unsupported inventory symbols, listing available presets and guidance for custom inventories. Unsupported symbols are never silently filtered +from custom inventories. Supported tipa notation, nasal vowels, and braced +consonant sequences remain accepted, as does whitespace between symbols. + +Use `lang:` when you intend a language preset, for example +`vowels(lang: "english")`. Unknown `lang:` values also display an error instead of a chart; a +positional string made entirely of supported inventory symbols is interpreted +as a custom inventory if it does not match a preset. Braced affricates and +aspirated consonants still require their existing display flags. + +### 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.19/_config.typ b/packages/preview/phonokit/0.5.19/_config.typ new file mode 100644 index 0000000000..6ccb1944da --- /dev/null +++ b/packages/preview/phonokit/0.5.19/_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.19/autosegmental.typ b/packages/preview/phonokit/0.5.19/autosegmental.typ new file mode 100644 index 0000000000..275f933d0e --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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.19/consonants.typ b/packages/preview/phonokit/0.5.19/consonants.typ new file mode 100644 index 0000000000..4638fef300 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/consonants.typ @@ -0,0 +1,979 @@ +#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ʃʒʎlrɾjw", + "russian": "pbmntdkɡfvszʃʒxlrj", + "arabic": "btdkqʔmnfwð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ʒ] are allophones of /t d/ before [i] + "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 = "" + let rejected = () + + for char in input.clusters() { + if char == "{" { + if in-braces { rejected.push("{") } + in-braces = true + current-item = "" + } else if char == "}" { + if in-braces and current-item != "" { + braced-items.push(current-item) + } else { + rejected.push("}") + } + in-braces = false + current-item = "" + } else if in-braces { + current-item += char + } else { + cleaned += char + } + } + if in-braces { rejected.push("{" + current-item) } + + (braced-items: braced-items, cleaned: cleaned, rejected: rejected) +} + +// 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 = () + let rejected = () + + 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) + } else { + rejected.push("{" + item + "}") + } + } + + ( + affricates: affricates, + aspirated-plosives: aspirated-plosives, + aspirated-affricates: aspirated-affricates, + rejected: rejected, + ) +} + +// 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, + phoneme-colors: (:), +) = { + // 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) + // An unfinished inventory should stay quiet while the user is typing. + if lang == none and (consonant-string == none or consonant-string.trim() == "") { + return 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 inventory-error = [ + *Error:* Available presets: #(language-consonants.keys().map(raw).join(", ")). + Use a listed preset or provide only consonant symbols. + ] + 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 { + return inventory-error + } + } 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.replace(regex("\\s+"), " ")) + + // 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) + let rejected = (extracted.rejected + categorized.rejected + + consonants-to-plot.clusters().filter(c => c not in consonant-data) + ) + if rejected.dedup().len() > 0 { + return inventory-error + } + if consonants-to-plot == "" and extracted.braced-items.len() == 0 { + return inventory-error + } + // 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 = inventory-error + } + + // 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 + } + } + } + + // No affricates to plot (e.g., Portuguese, French): don't add an empty row + let affricates = ( + affricates and (affricates-to-plot.replace("͡", "") != "" or aspirated-affricates-to-plot != "") + ) + + // 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 resolved-phoneme-colors = (:) + for (phoneme, color) in phoneme-colors { + resolved-phoneme-colors.insert(ipa-to-unicode(phoneme), color) + } + let phoneme-text = phoneme => { + if phoneme in resolved-phoneme-colors { + text(size: scaled-font-size * 1pt, font: phonokit-font.get(), fill: resolved-phoneme-colors.at(phoneme), top-edge: "x-height", bottom-edge: "baseline", phoneme) + } else { + text(size: scaled-font-size * 1pt, font: phonokit-font.get(), top-edge: "x-height", bottom-edge: "baseline", phoneme) + } + } + + 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 phoneme-text(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 phoneme-text(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 phoneme-text(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 phoneme-text(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 phoneme-text(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 phoneme-text(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 phoneme-text(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 phoneme-text(pair.voiceless), anchor: "center") + } + } + } + } + }) + } +} diff --git a/packages/preview/phonokit/0.5.19/ex.typ b/packages/preview/phonokit/0.5.19/ex.typ new file mode 100644 index 0000000000..eefddd5d06 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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.19/extras.typ b/packages/preview/phonokit/0.5.19/extras.typ new file mode 100644 index 0000000000..bd2e0a88e8 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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.19/features.typ b/packages/preview/phonokit/0.5.19/features.typ new file mode 100644 index 0000000000..d1064d2640 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/features.typ @@ -0,0 +1,2140 @@ +// 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. Center the matrix on the math axis of the surrounding text (0.25em + // above the baseline, where arrows and slashes sit). box.baseline is not + // honored when the body is math content (the math baseline wins), so the + // matrix is wrapped in a stack first, which gives the box plain block + // content and makes the relative baseline behave. + box( + baseline: 50% - 0.25em, + inset: (top: 0.5em, bottom: 0.5em), + stack(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", + ), + "ɡ": ( + 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 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.19/gallery/autoseg_example_1.png b/packages/preview/phonokit/0.5.19/gallery/autoseg_example_1.png new file mode 100644 index 0000000000000000000000000000000000000000..3740a305bf72f0999647a5256ebe61b293bbb87a GIT binary patch literal 5355 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 E0Ou;dAOHXW literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.19/gallery/autoseg_example_1.typ b/packages/preview/phonokit/0.5.19/gallery/autoseg_example_1.typ new file mode 100644 index 0000000000..05c755c5a5 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/gallery/autoseg_example_1.typ @@ -0,0 +1,11 @@ +#import "@preview/phonokit:0.5.19": * +#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.19/gallery/autoseg_example_2.png b/packages/preview/phonokit/0.5.19/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.19/gallery/autoseg_example_2.typ b/packages/preview/phonokit/0.5.19/gallery/autoseg_example_2.typ new file mode 100644 index 0000000000..87c7b3cbae --- /dev/null +++ b/packages/preview/phonokit/0.5.19/gallery/autoseg_example_2.typ @@ -0,0 +1,22 @@ +#import "@preview/phonokit:0.5.19": * +#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.19/gallery/autoseg_example_3.png b/packages/preview/phonokit/0.5.19/gallery/autoseg_example_3.png new file mode 100644 index 0000000000000000000000000000000000000000..6128c4a22be892ac025f7ce40dba6967600524f5 GIT binary patch literal 57914 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�ZXyMB=~d$G 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 ^7Z-1LMKhxNl? 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 literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.19/gallery/features_example.typ b/packages/preview/phonokit/0.5.19/gallery/features_example.typ new file mode 100644 index 0000000000..ed0c4a5b87 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/gallery/features_example.typ @@ -0,0 +1,5 @@ +#import "@preview/phonokit:0.5.19": * +#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.19/gallery/grid_example.png b/packages/preview/phonokit/0.5.19/gallery/grid_example.png new file mode 100644 index 0000000000000000000000000000000000000000..24e8dd39009a028c006372b04a5d3a38e490eb4e GIT binary patch literal 5387 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 literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.19/gallery/grid_example.typ b/packages/preview/phonokit/0.5.19/gallery/grid_example.typ new file mode 100644 index 0000000000..530381718b --- /dev/null +++ b/packages/preview/phonokit/0.5.19/gallery/grid_example.typ @@ -0,0 +1,4 @@ +#import "@preview/phonokit:0.5.19": * +#set page(height: auto, width: auto, margin: (bottom: 1em, top: 1em, x: 1em)) + +#met-grid(("b2", 3), ("R \\schwar", 1), ("flaI", 2)) diff --git a/packages/preview/phonokit/0.5.19/gallery/ipa_example.png b/packages/preview/phonokit/0.5.19/gallery/ipa_example.png new file mode 100644 index 0000000000000000000000000000000000000000..e6e6548ec63c93630ca84f8a15901e88ea01b1f9 GIT binary patch literal 28220 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 Ks@JR9Mf@Le$o%2} literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.19/gallery/ipa_example.typ b/packages/preview/phonokit/0.5.19/gallery/ipa_example.typ new file mode 100644 index 0000000000..0ff311b929 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/gallery/ipa_example.typ @@ -0,0 +1,17 @@ +#import "@preview/phonokit:0.5.19": * +#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.19/gallery/maxent_example.png b/packages/preview/phonokit/0.5.19/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 z%Ij|6i#cT%jIbi+0ro`HAczq~;XX#$?29sRZ(aG$R8wG*vgYy+ZvSv97FaucxJwTt zWdtZ<*!*IQeNHHFC!xuk0cGYLgX(R3ut}Hii8-niy(Kpj4d9#GxFSGrue0-j3nS}C zK+q>#S4&W>@Ac2GG{v;t!R1BjU;wL7G&20n-RA6Uo4$G$NI?$e|GaR~KMN2Wf^pDI z1ZWK}qQ$ojA5zC~vNVMM5JHO1FPI8gp(*?$kyn1S_{BXUgDF<#d9#i(QWsM< zA(MiL@>#I_h1n$KNP!g23C#>Od$jWm>o^p2c3HSzZb=asTuJ6Q505LvOTBKL3<6sp zNOo7ahw+J5)VEb%ut{zUd;8qLWwJ(GKWwK9*m}CW7Yf_!Uu&c+f5>nd0B6-Ia%(DZ z+>@cyb$jyA?UxOCcz9H|c>)#HIy3(fvMTF$G}ftQcX?3`LkpKdWzBbRDuOBhc#JOLRW=I zY!IzuGT@P0JX+w=BujrWK#VTgLPM9?xWmXAjzygg&$F$Q`@D|y_UlIn!t;C|XC?U| zyD+f7{Zdavy7;j_@O|QriT60)Dz$CM)+fr~o3EOFTC5h^n4UzIOP^l*1xT0EkLhhsPnYWZp9&9nQaVIH3`~uqO4oF#gL`$j<+6lP+S(#APv~w@9 z&n93)q?T5L4{a&)68iXEGsrdxMyjWHJMS&CEV0hjMZ5lFs2|32`c>C$+^+|V_;%pP zC-cCKSib)rHueQnISA#Idy)&Lf(xB2ZHnB4hT^( z%CFILMxIRJIJnoiVITsZZC!lmPO7AOAWJ2vP{_#)s`}#vq#vH2duR=WfoQM8atzFB zVdc*3K=^&hhRw^&$C&05crDylC1i7)RE^7NVAXv6Cs%8ddZz5fUu*&^@DpL4r;z$N z)RV--zPb?%aM5r&ZTE*wY!RQWJd)$Qs{%(9nS21o#uHH&y$bdt=5QvT|7y-|{%%kv zqQ)s|zF8z=2Xujy*7lI|9;_qq@KQm`GUAHQuRw6(+3Iqi$8J09raQ@_OH_YwXu>@sv0?Il1y*d?vCuo2Cd@$z+e7F`7 zd63^+{Oy<9>2h-Vp7%i)R(lko_~*4l=;D}pTeafu@$O}OwbR3FBP+*)pbDW*ipe{& zG+(Lkpk^Iv%@&VV7H(|RUW%!rw=d^cPfZyhW(Df)57t%$Pp#u&a5~PFFJ-GimRr=t zP}bc{ONe8`_%J`Vzc=ew=fhy!md}P-p7hj2sHkzFoc(sRe-eArnI%8|8jCe+s{ycmjq5mRa;R4T}3f5XkkfNfc3VoN$rw}6Z!@!_T~FMw<`{PR$ zRT+gBWt}nhkWp0_t4=-V+H*X`q%Dv5TIi=4dy#*&*ze4 z0&J*6kXLJfHr%}0y0wZxhwpBC!Q3LLm5%BxusGzv6hBGAiMAhn<^Sn6MSU~lB1U#S z!5cFVwQ>|Zlh&F?J%VbGMS9u|nvdxDMh_b{iJ&L9@)O7DlyQi5FPO3K`r8WBlJPXu zTZ5h=b*q*~ey$!pz~wx|EaPYH33A^CFCCITXvi$gIHxF!%;v zrhffZ47eH@f9;u%QA>-<4(ltMHEcwl0Ex-I0@Y&nSyS{%mI<*((v3y@t6NX}GxdVh zZ$1080&4T9@BME8Enmh6@#nm=->09&m6!w0K)skGuQp3id%i+PSSGe@r}t$Ouc}!7 z&}}m1Abb{h8@@+1*Kf{j@0PMzV^3mPD7}3SLDGG+o#we$SK=j}FP1yR&PshTf_e?F z$;{Ys2(uoV3rK+}M=W+LI6<}jlNR?#%s%kQw#cw|wPphI1m(5phLX0*`X2(*9}aHp zm9Cw^aC$nZ`$n1InciYmHuhMRR(Bi`V~t$ED_wntf<%m~-718I6yp?m;_>qlFEq-> zILLeB(@Q{*U0EI)GqNTNxmaWV8L?VKm)_)Wt{Q-W_k^bfuZ5mVVBxx6oL~@E>aG!E zTb@Z~Bv4o_um(W`tP`V7=L;HcP$mjw9eaW;dwTbMV03H&Tt^usZGf z_}XWW*V)yMw|70_u<_Qs`%QR2;LU+rK|Q0(vxuSQgjYQ|JSQ+3Q1l}fD)9cKz=QFc zSUr46iHyf{`>?Sb_JXI&6DW$rOFF%Sp`5X>eUY8k|DB4aqZ)o7!dw(~&0j9-;H7M8 z{u}b3reXWSfBU=z8=QWia&V`3Wk6k>ee>_h+0P`*8nmb zNwXg#eR|y6Np1U7?_4g6g{Qvlm=o9HdNtydlT6^#K1H^&3~oSgfhJg&cHPYl0HY(k zenQ0jP&_8zCiobU-yQoOiw17l1w-poAV5QaH%ie)RVzL*f{#j^klwjR2G%2-rI@UH zDK>^|7FQ3k+rzR{l0a&*gjD+$S){kT6yAx=$ySQvi}i(zF3hA0@4nb)Dik#JvkK72 zL_Z$--3mLru+3|aA)ep#L7M?>o)7=Gph==qQAPV`WY2FT<`)=m4qq;~N?KxqdE>+Y zQ?V=?^qA5EqU~-#2Z>EWW9Q)_Q&qb9cuHVqkm6N^hbw zn7v~(JkC(k*iDQfwIzZv)ccJ=8rj%S@=FQ8zOGnUY!#+H^9{8Xviz$zxGeu~T>n>> znoVoFpcpwJd#wcg9B*fs!3QQ-FinSo4}UO`+UKB0?Ri&#IzDYMe^oX-jy%W z4?>*&auL3P%*Hw8vXEe5J!-8O@iuRZ@vye#lT=JSWzmIo}$<`&Xd_xYS zF$U!LzaKK`y>bt+lUut5*S7HojDK``W0SgV-G3u+vE;c5Jq$_aP6tjjk=BCyJ87^` zLsmdfBdWq|Nz%3*7TAz=>VuKa`;YMd12j{U08)*Zf<+~vOhm*2kK`7C#vuja5^GGH5i3j2P<@8H~RDr(IDIB z6&<;_fA-wGEtEe3i$%HRVO=Rk z@{=TJ=Wk)=>Nx%?Kv`!9%0$?v4~+Pwbd3l5+rz0F=DK}g5KBKAGzYY&9hGV8rhMuW z>-E}emA`pUd&`4hN~Ks0#^3RYvWgv}F6pE8jW58IA7r2vMjGdBwCE|1h6vs+S4&K5Z-PiOK*DNKHPkpqV$>t$4o(^3U@^QK51T zyL_M&$ClAk>|pvcC=#^LmbUDrD9Q#TkVX|gqwn*e1EfW&wK9f9>-gzI=Gp4gA9+iO zhCs=0OX&F)rC4U>>EZ^fd@n0iam2eUPIPNAg@*1G+1TS*A6Q zL8}YzpsZiuc=&x`Zj1yYU4vqrFz6+}k*nMTJWuN$2(WQ3-L_=ULi-9aZYzBOBt}aKrhuk%)Co1n?}?0ui5xl1XH3nv zv)id7+m93ZRuE$vHD&lL_)TW~FnJwRDW^EW%z3nSOb&s)HtvC~n@znTK0$JIB&*~U zTwH;zK5&5ejWv!VP~m;yIS*bIWDWW zJ7AyS4R7nl%tSjKnvp$HwzgM}U!lyVZE+fYR4P=Ostuu2(%#>)xR@GWmN~a2LHF9&okoF`Cj*->tfn zGn#4yZGLmEE6(jx@X9hGw%B$eoKy0jF}drvNIAG|7PT<#X<~a@~t$Fys{l70LL8bM0 z4V5;H2>gA}zQGmFLi22BN{bhiKYz+YQfoW|9F{$VCNYBg{vOYmw7f z)^bUaU1*R%PW#qNbsBV#-Cb|&qs4lzLNqKPO(836OvX}=M%!F0m?~S8=CQoBe-O4V z4Q0iM-M2NXtX~98GOHUjVqTpH%3C>mYtI0$)9NsjK9nhk5GX}ZLmuR`e=@(5C%eOg zLS@KPcHR0BlOJ^>!=?}(B&DAHzx2UjlK@sjIHn{^x^uGB0` zDyEHF_A?jII$k6A`$a~A_rd7+=y|jFx&Y1Na|n~hAvO)OXUKmuSGdbtVgyZ2!$Ce^ zck5O-UI1i;9?h%65|V2Uy%mBQ1TRpP5fo5`$!dS$G+L%e^}#Ev_8~{DaSLa8Whh(d zaRpuCYdUJhUJ_pM!ed`ac^b{p!;%b2CdG_PeJ$^=Id%17bCnNaq@qxDhT&v`jXnjS zNd54+f$+wU5w}^CMStmBW%-qf-~xR=+SXt>(7PifSNH8jPhDsUs#vsC04--uV5oe$ z!{(h?;pX@%OVm9AN}kI>Rz^hZaSl4)&@&8npiJTG4|dx(&?Lkt2o%m@idQPGje2fW zun2?>95f=+vqL)uKV4)nDjI~=LbWxF_guLCJ)@!usBWyb7s0i^ASJzZ(-$sgLhQUy z0H0SNAE?Wxac`D7LyHjy^S3Ih(R~9$rWY&RK4>+klQQB!2GcVf^*BxKDrZ~{%WwdP ze=(t+cNP1kyZC7dpqIRon&Z@EYHX{!Y-V^ljk)z0>+C3D9D)evpewrsz#u|>k+X;9 z+-l44Gop1O?ZPd#LrwMGMVcR=>;!@IJOc)kD~lG69(oO0A4Y?=<>8;7KB}z_Q)+tn z#fA@QtHI@bsJ#tW_*VNaLp)ja4Z3AC8%|t$khc#GNxO`+t;k9%F4Dm@phU{hxZPRF zSGe%l$Q*iOpKVAD=BM#s8R^(Ey7^$bxueqO>LuOo?dJ~0H*{kIhDlfIklON%pX+xq2uza+5v0dxgDQCx`UsV=lA%FrlnyA^8=#GN>}gSJR}!vn~@S$knRbPScp4rg(>MLWvF;%5Y1D~BLa zh(ANQNDYNW5RcJ&Fci!4=?jFb>-n!0)hGa{8*tq0>@Mij0m7+Bt=(2ri$E7>cVzg; znuRkEsqn5e<1pi+WxI|6v&#YlI?c(XpaQ`3;FSUe%-O8X0Qp-3*+KZ-eGT?G;JFeZ zg})~r>JED=_klNw9(k<4Y=SpnIB{D~am5EN<@Sd})andBNisFmA4Yml_G&(CwNlW0 zb}fcTty88KscJX0TZLN>J)8|q*0Kytv-2ymzil@YJ3*-RB5LL6#>DJR86o5X1z5ma zSUk!6H7{&Xh&cd^blz*ls(mLh~kZi_E}ZaownJgglyi^{IqhsX4Z{E!xgO3ll@hx(RxhbG&KaeoO~3 zp&h#svwXG2J4d=y7*>ci>hlEB8?`gg%M3N;0NNrNd7^s}ge{O583H^s)tFHBk6MlO z^Ml9j7yTIjOh8FttuGk9B~4VqXcn((y_wQbZv_kO{C4dE z2?#i1rg|&8wQrl6(LVr4)(=I`=6!)>l^mj>$ICp+c2q0JzH=I#Hh3i=5J82_HC|&! zEi)d5A-3+X*y9j&hMI={v*WRt=N4nM2eHnyeUj4gl%zr)FTqjMXDr62G5>QlCBHtO1QQKDw| zneOQE$=wMzKl&D{MCjjv_QFW^>QutfMu<&y0EQTtdn<~eus#|FOj!}MMk7f>K2psQ z_xx@pQWTDz$(SQxx}g{GpdSh~HpdGc2TZV3sR%s%gp6QARv2%8{Ecm2Y?b$EBIXN~ zEgfgf5(g^(NCecE5ELSP4F5rZ=

{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.19/gallery/maxent_example.typ b/packages/preview/phonokit/0.5.19/gallery/maxent_example.typ new file mode 100644 index 0000000000..7368611fb5 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/gallery/maxent_example.typ @@ -0,0 +1,41 @@ +#import "@preview/phonokit:0.5.19": * +#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.19/gallery/multi-tier_example.png b/packages/preview/phonokit/0.5.19/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.19/gallery/multi-tier_example.typ b/packages/preview/phonokit/0.5.19/gallery/multi-tier_example.typ new file mode 100644 index 0000000000..9a27babc06 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/gallery/multi-tier_example.typ @@ -0,0 +1,18 @@ +#import "@preview/phonokit:0.5.19": * +#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.19/gallery/ot_example.png b/packages/preview/phonokit/0.5.19/gallery/ot_example.png new file mode 100644 index 0000000000000000000000000000000000000000..61b0341e0c7926faa74139a8cda3017cc89d0a29 GIT binary patch literal 22397 zcmce;1yCGa*Dg9hAZU;PfgvQgTcbgP!(hQBID>nzKybGif-^t@!GjYtSbzY72loIG zTmnIZpMkvZcj~XdZryw9{B^3Q8m4>gwbxo(daq}m-O=i53Iw=dTo4FE08y0H1c9)i zAP{;hHtGWCQcb}R1k#g*$VzE@&uvc%6N6~*u4>{ zEALefPxX~uzWV!@-9n4w95O{d+{h>ZW)7pE2O!Qj?POUjl9sJc;d2^7$pE{Ze;WG}DxjWaiL0Ma`8#>51xJrk6o8#xQu5DC;M zk%E5UTkCCwC`S;-XQBY`Px0+3q58kNZzF&R9bFp@{#h_4^w(>z+JcuS&wA=Up<-ZM zWl$4v2s2mF9h!=W>!zUWL{!X{2|Y-=*hBpbYa+?qNe5!vu27=sT%B#7_E;9`Bvi(77V{3L%FU2y#}i z_Wcq|q;_|3&n}gGD6si4&w%RS(r9Mp`{-8+zWbU-4CTILW2&p45zRTC3S8nBh@~Mg z{tz;8$FSlkvYefu^-T>0Z1hUdy5!+!w&vq~5-U>Vi#JO?k(336AvrqtakMNUweeoz zbg~m8Z1!4sp{zXd??!NqW~^*1_4s+x4AkZ6u|t~TiOF8%c*{J<{YczGOaEl?(USX@ zCqH-mrw@8hFNoWAfA~1kU7YC|`w%H@Fij()Aa2?N^}w$$IxyaavIp_V-pOnlqgq5a z4;AK=F#J+xc>NF~_# z19Pn-CkOu}dZ~nDnEIsO(WdS1K>h+6n+vqa7cKF`;(Oi=Ff zcIAWTQ>6xTUz#2i<(b?6m%niYg|O6uz2L%imB8Gd zPR{AI`)t|oP0_dc+8CdnK%!%MMy=-ZM)5d{B9EgHNCc81eGzebKi+NHE;3u4EnT4S zJ8iOL$UV}~@+(&X`O-d;n4_#t>m!L_vY;bvOe<^_x?)j6$>VYt<=W1tk88Kw4YwD3 z-oQQ2+ka1czU5=@M1)fm5}9u|=k`E1h#^X^SRjO)>rw^DF^SRzys2WTigNYUjJ|Z| zB2(3j^E|0Qror%$=-OEp-@fg_vz zJA+KW*~m@w5w7%s$C&E`6#8Y?w9RbpYl^6vWjcMgBdpH`UW{{(b~^c4a`mo~KGabs zNL=Z;b2vsjpJhIf=7Z}+s~Y`VNqCu`*<|xXbeiHhd05#5n%gY3X^>r)<8NFoYi|8d z613%im^v+q_PR-P1L7rX8?mrIK$cUd(ZlT1Aj)d*OrTd6C3^TD!|ER&7(Obt>=~e@ zr)K}LQjxeY{o5_cX2`VO%aoV30Li{XTPO1yvrp$bbJoi3`6KSS&nO$8f@|*S3nhx) zDf7f}z=wm#y~Ftj?>2izfDdS&=&L29Qol=dB5ALrE`6f1bBLkS4zg?~>Ycii8x|`==+}p1qh!k@ zy%o5&9q(7Uxm%@3Y@{~mH8Zg=`)DFb)#v2Y-b$9l&Qc@=@)t5!73aG?c=9cQWC}r# zy{+8Jj3}9b@iTFg&p0r?2ZWE**R^Ef<{hthIpX@_XG!1ek>%b$3A+D=TQmB zd_C#-_Px!>zKy3=qnx#EvlLnYKhkUOoHIH<(S7?wXKBcw={rO5!#m`vN_;_{%yomE zhPs32t3~j@b0}2eZbT%+3LW$fkb!T2)c7FdDN9$=l;K!52a21>t5w?vHMY9~yx#`j zYh=c}2PTNaZTfabFnZ67lNxy@7AR8FMsW3)WEA3<9j{&-Qw^1U^O)Z47N&Gw9n%@} zkRS@f$+z+GZ+2oxP^Sv(ePB!|&FnXU9ahztAKwam9wo4BODH@c{s7==IVWcBEJJ%m z7#<1-cn|Cg@7Cc+7gas(OuPxQB5!XfyeW31u{%ph{q80;Z_15U`Q&FGcB7yq@JqDSFn;tRP+ z#I6SR6Den{_*NCSKnPXmkCf>XXO*}+w1=;MWa3Bu+VQPFj5iwNK@SU(<#h~XsBWj7 zhWwcCXHj7)aE|)JUu}}lIQ_e^CSES#tCFR7FXLsiJ}F10;txtQ_P(UYK}QUYq_O$H zHm%vmAfTme_r(*-$SQfxWy8I(9Nt@6N1Hw9l zzD?6OOYTWOUll@3a|Xjpi$GwtE#8HFWA=VnnU@H}s$RC)rS~qpQRiN` zUY}D*6HwMt4J28?&8J@lkId;BC7EkbbZH6vu^}n8l+E44ru3r0HRx_-NSnh7x69w7 zp!-rBd`~J3(rF=E=0R=1-&dX}5MhNclk-ZX@d&NHe;!#i*N-%c!6H8u>N~`0CSI)> zD9p7R?A~}Q6Z8VArgzs0di4w4-YP9gR>v$U+U}y?vH#^EntJJ<=4^pgU!p17aQZ(@ zsE!?u0YcxBYeMGjJx<(=^{Pzb2FncRjkHTbMW}4?SG>^81AePu&Cg@gPH*@JH4EdG zoDPvEXsqQXOg>RnDlShjAh+kY{#DeAOpNe~dcroO51j#_p1dVUu~J8TP6ackeMO6h zGoE$naKL8)@nYD6F^%@e@22oR7L0AX#DO@hphuSP08}pV36Eo) zWaC4ff;n2Fs%&%Qf!{cyQoYKE^COT4jC}tkrYRf~75t#ZPQ@8&36CiOlCdopXVwnD08<`dGK{*sH%J?~-DtDHgO@Xg`xV9dEIt z*YNIW1It_nr6`ShYdbdPASV>MbbU@}e6u}_qom0LB8;Z)&{&jP?yEw*a>o-xsA@i| zLv2Z`G$~Q@T-=0f>-TMJ&=Za{%U^eM*eC^bNc`*Eb9$R>`6JF~4CUD>U1eZ`5kpen zJ%|OYo11`!A3+8}Ndv!KK~Z9M6$e6O6@XUzukVg-vk!j}1#L$joV=?BHZ18d`XD&Hsc#zyXW$Ky^bWyq zB3(Vb8Zu<@D;-5FC3uc~bd8D1Y;jPreo`$bH-RWy@$zAC=eeVwvcJt&smbM1q&hX}Rn9Dh{~0`o z#jF%%^~4KP*bod9X~6J1)3+~Iie6&|GUuGCpsSY$On|64nCmK@A`^ZitE_vO9@GQ! zerTVeUHc-xUkSRm@oIes*DU-_U=o590aioIi}edcn=?}4&Is~+mT>g5-6m`^^C0m z;K4&&XMO6%)yBg#E9{>!rkhm{cyr_Hn8mYm_G0Csg|t*^ZeO$d76n&vEO*ImpzG#D zh4GzRW+yF;%!S3S@KZi8$ewv)nAFibG0adojiYas#g=~+x4JsfYOD;S*a<|Nq|XUQ zZp-OT4};zB1jA{g3qd16F#c5x+1f0|+yxef_)q>Fy%vc^yGL1!j|Eo&Qnpu)D*H1Z zpoEl8D!yh~u>ri=sV(zg^A>e1*q)Rh?mMf9Z3u@-E5MQYsZx>eRgS{Pd07_)3)|@Q z!(CKTFYD$Y86@l=JXM-pA8ZhzWhNXTD*@6ngx-h^+47Fjxj2JW12gi4Dr8M2*#z3;#?*bl(KAKcwMYvUnpER)ySD<9EYp zUc*mQ9_&##syLa&qAX0l8?;{$bXbMn?!8iad9%QSMz$RB@4g zsED$XulkvD8xRRHqq8#%Z~wt-tA`EhG7&sE7D5@hF$8+|12Q(ECIlpll(=8M^2$^E z;cR)@2nJ!EW$OQkx_9)jFfo%8L|rEM)M0Rq#xc*&WzX#eJ>1}ji4h;6{)Q!e(oU>@ z0THlDcUldEj<9xXK?{Ax5o7K3E#R1^LN_Zr`vuS@(1Vu%PHm*Qn;=C8KTcVWSO zYR|t4s!omJdqniRY8qY~>A1KfWKAq2-@{JR;#$lLN}hAbdJJXLhPpL_BrVZ>YzGM~ zNBn!^2Bt85z1|##(41)feOv*sK@~kHr@6NX zm4&FSi3wOoJ%9_}<@ok|^jUjSbD{=j^EJZNM&xqeJFZdeIytFtWo&Wqh0*ZzoXAq3HG6?=pEi1ZguCQLxI zAMOaQ`kEP$O~uAS^MO#bcM}98Uob)E;bK0N#)~wMg$keI>S_uWt_Azmgu;cGib$%9 zUk32Pj`jlvc`)8SlgVk(h4SW&NWHQn#QdvYLptCbF-ZKo$JoVAn6xRiZ!^xNsSn4ImdOc$#zHcu{prD|;gP~= z=>Pe0ZM~T+Qu=OYCDr)-a9ca*L@iaQY1%i&MM5L_GB8+ zh`g%ByGh@fT{9FS#9*HT_TRDLPS=7DisY`cdmdxAd~<>pzT-*yB?+z2zN34FKrNEZ z!8Jno)FFh6YS4TBBV8dO`~HJA?Z4EXpa%GwK? zN?4VjMp?wZZy{@CbDR?Rt5UUtSkZPNdDkZ=f223xsPirlZfic3+QJJii$JbxvsUXoew}| zqU-WO9Lt@{Z%KWw?03QfEPLr$wwVY^HdhVhwt5=wsOWAlK5 zd)Bs0)uC?l!Q(_V0Fw(i?@%7~q&SY2v>XksU$y&su!Monr)b4e#kFj0c*c!a-QRg~ zwj&-Wh)%)uvHqm?!U?s;dD?k)%HM#)GDzp&NIg<8p^+`PN-A=>{DQ!muN7Gpg>~)y z-e8i)Bl@yiAt^^3+p{-*O?~ePQkfpF%1)4}frM^<1rSVXVB;I3;4o|$YJ0v&700FF zUc3k2D4yX#YC*9=@G5wzpiOY3egkPPaQ;X(XFTTN&RTy(sB4gR)*V5_{^cA|dfgfp zRYIW9DmJ?Bg6G%gzPuxd*x>hhz=kj80@(7b8i*Zb?Sn$E^5!Y(l#;6b1Slo2I^2?A z-qhih5K|FcGQ~-X<44t_zwjKi@cHE#FD^+f8m_nZGqb4p$iDWQqDNqIem3}4 z-j6T9ROp`v1~euOJvPpNof&qaGR1i@fmLl%y3{%4*P%R%j<8}PIHWY@vr;8EX&Toc zg>ShSsNAA1@C73T()G_;dpC^>5=RjOXzi~wUQvR)diRV<$l%2(@-^q@7FoB6> zyojlByPQ_3b_JyJG(5)94pE~i%R_NKFZBFZu z9^8jAT%p$;s*htkI5|c$NGE>x`OFT@RKYh)-nw{$q$4o2H0zq0H!d6PL=}qvK|HZ7 zeDD=sm9N$i647E0^Yb{|%LVG2;|Ums;gI?e8A%b=7MyJzi{g%Ui9(Wb#3{|-HVAYQ zAB=n`-%Ol;2yUtvEuC$GJUa8e6aLzN0#W*%rHu6yP8<<3(sf|M<`6X`Yi<xr9xY`^+;Z;8K<8~14AS{eG~Zg7?4 zOAlpml8>I$Feo78v-~B}2uD8BjgsA`wA#XDaH1&dmA!usn2&I0vyf!K8UM2wYR1{6 zlG)N=dHKZ^YMtv^_6`Y~Hfi_l#JyKh7nv7iZr^8ze-!HXrwH{FN|x&CQ7muv+iBU~ z-kcLkd6`^V^d}eC(!ic`s09oa;9$?GY$};fsi-E|xnK9;#1gXRw06pI(o@NYR5Gmz zL)(LfDP+FTIrMf94EU@~V0m0ZR~!*2N<>brM+uGHw;u(2ougJ+EFL^x5ri6w-0uNM z$2WEO@+Zt=Fb+3*>^qVP1U!B>05z+yzM^t>lKxJ_x{WK>g$-%Div2t;VLKRJ!s4eQ zPjw(h=lpvMX(@E~b#vlkm0~4<37eb2#$$kSK$gD$-#2f9|>O9%=m{5u}>Y2y5; z5{T%dRQ>oPhb*Wda@ilMxhwTuIS)=5DIHRTtg;DmqDdG0W+n{zY-Z-&dgmsE;2MYX zGua?LX$_;oThz(8qHW0Bc6~^ItED*5 zsBYP+SP7JIHHksG7ZLpYGl;-`s-#iU?H`aI8bphz*<}9{P3tWSy7&WFiI6_Hx=tV^{y&%nn2i`~0J&$G1giDdzKeNJ}mt5mYspmo30H^Yq{IkE~uPMYCDAHIzngBKnVn`v&sNvYJtXcTvmLH@5D7gA-W}I@k>_ISm|%hiMA!p?0XyUYb;XEc6xsOC;^!o}sbvX{*}COC@oqfUZsND_&7W_mxvI7Ne&MpFa%t8(epHUA z+^0LTVRUlBD%%n%7)ED=_o+QMB{WDOi?$08q!dh1NQ>Crt|G3X_G8v{7}}VfqS5wo zXok;3M{`QP{}{16#vSlDBFd?r*-GQnYX8bW{qoOFjX#IAqT-Fy^>uROg4fa|aE}7h z=iun=PESpTp75q;)cvhZHPlssWm*}UbCZa#d3cKz=w9%Kx2lZATM7YQ0!)_cWyTw* zsXgPK*y1OMoCoXl$IGO21|zw&sI#}%xyxt^hI%q-BOl3knj~a2lhlh(tj@5*_adB{ z%jq7>OImUYZ0f*ZCzb?U)|sqXMjxh$XenT0fycf2G0 zm}H|Qp^TY$&Tz-7WV9HRj16Z_1JGC=co)70AK;Sb?=P!pBJ{yIG+z-7$~mxtLEE+r z{Ju&o^H1#v{vDz*vbu?$ zks9I6MX2t0kgd>#l!e8Fphwe?q6;xDt3M2H&zBYwk(Q>6PAwD;wVrvbIQ|eWy#pC_ zf9ty%$A{;n{VDqa&AQKp|AWsIhbDW!h3GVYaug3~L$Rm_{CC&-w(si=LME81^}74i ze*0X{7nu5dCCO8n&1IiNXxSSyc0>j;bP)6?jv{LMb(@f>IoI!%?59P4Z%U-D{9(bK z-Ozj#PI7(CO&C2|=;$((UDFcs!$z@~AFNd{hMFarO%f;s;>v@OW+_zkIZpk(xb*`q zeM7oX7&GP~w+=5`z36P*hl2Ii!Pv4DBw{$J`0F=#pQzjK-^mnI6@dIfpV12Rr9rc4 zqEfv`D#j6=ouK>|)`Cr2eE4kqC);@#2V(LCayd;+ojKKI<<#Efdx43;#5-eu5|{cj z*SP1k2PzXU2P?N-R~_d9{T0b4?Wc=W1gw<|vAQO6>mEIrhNjeY$3?1Q22q_T?d zVa#ZOQxeMe={bd+@u|4pFowxIt#dASk=k;10xp^Dj_3=HItJh2jgfc;VAkW*EUrd! zg-nM~G1qQqlm)3*epE|AKiBzY;;xf#!oI|%oE$@m7L{PziwZppzlr)`eyrR_}nV>;(kAzTUtd-@_)~gU_c94W(If)(qioc?oI5 znavS_H5mL@?*K<~Tl};;U>T@B)?zF&2w8>)<>UzMJkRrurj|dMx+Cd`?t+LQ3 z?)V`~+>v|V+)DLj&zIx#p7}40k-HDRtb7@c86UP(ER8C+6Od0#_8WjDFbur~N*lE$ zQ=;4?${uQ@=aCg<_r>cHJN&v>`Lgu3O*HVdLIp+aP{BezxgmJmaw7gEf-qq~-`wyv zZn+ak1d0}cqIn?j7g3Z8gx!vp&Dz)?$&G|OQ|O5REmXHu6KzK>xwNyN*!P$rM* zM*rZM3MBEGOF5B(FO^PkP7`gCj%~+0B`QN4@;>S)VvN;d;BDL2Q}%Sa`o}41pV;sJ zLl60fJ95kR_=j%tuOTS1hxrZRBpUeF-hap_f6YYEP;RImC=LpWYH~vXsy+$g<`)jy`))N)H~ zxzY8Haeu47Df(ZCFneq&lJ9AwkPqX%FWi+p4B!=_h*+S;y&go7NPoNsjs^1Up{YS4+Fvgico+ePusB$LB=nx zR^rk*M9x=d`nMDo^y~2`vo#||Kbx58;8Z@(tu8F6(G-of++~u?%*enC7%i-W0`+;h zP%pco?Zx@(x7cTj&o@_KFSNe0TYmzqP6||9*fl9+ZfXhzxa{v6^FYOzo4Ecdir7~6 z?RGThh5A2Ls9gD1Z1$y=$2s+Z*C>GnzBxW}Pz5R>C-PF@%}4ScMBwwfb-FlVmZrD2 z(tM+c^|;1d_7Xz5oinUQabo^g`$Lh#cCbFVU3?%j&12i8y!A;qpfw-06i5cehPpWp zhNfhs^>^>jR_kD-Q4c7g{=UlgL86(EFP%bIeVh{j(;cb?i2k zuhaxwj`XxEDxcn0Gs8!sJ}CU4GdPyoDe^gZ%YG{LJT=ujuH^9%7awe_Lc{7Pca8_f znO0%6d++FLBUN;mugk)pFN>}6Iy$Q_9-3Y+eP_ac*d-avBT%VtooY}F&Gn&G?E)AH zs4ELNsi>99`*XxM^xzXPV66OPs1CGDDav9wd zEbX3MJ8gMo(r&W0@N(xUFv}(|vD$KK02+A+!woXnIz2tJ9lXG%nxDyK+36-HCjP0P zB7_@qLSE0Jzcl>1j6a|>s`P=}X9Y(W0B6+k=R81b$>=H_=Low!RLUI(EoWkEzI#6m z`Z!?3B}^31+-RW}c=1cW<1#19V? z(W|gr)2IK|Xj};wuE}qHaHkxgJThNGl*1JzyZi~iV)EjN_Dm`=qfO742->N+rk8Pz z+-a~6Y`8nv(v}KOfWP>OY{8&05iG11v_4xkK7VcUu`kA(A2C;1j22_EHuGiFm#FG> zsl_WW&+7U+VESGGQ@G@0;HpA#I%XL*y4fUR0jO2vz%#4#WsYw@ALH3yAE-;O(QPNG7^T;>9>E)VGPB{(UtB;DD#bBQuJ!PRFbk$Ls zM;q|qJ=m3vdBO2SaMg@vo2q71vV759%!FhM4*S zOflP3T63{sHXrRfAr$>_B1t?c0{6|9D6;O-YTrcPiK^zUHoFznC>M}9S*xjgSntO6 zaOa@WZT=#N+jErS_p3c^;(18ONCD0_ago?YZ>QF3q-DGe)SDTngVCtKnL))`AyhHS zMl}cut+*>W!%am`^|Jyly!I?6v>M}pKV(X9Djq}U?|j^A*{g?Y6<=7wYroN zCA~bj#7NayX<;jorhFiMGMZ$~p?LO(Z{^*fkYR)4T-C4EMlE@ygvsNQ8LF~S0awGrl-P&$*#>vv!Vjcmo!n5$y2wAY))27U4ziF)d}EVKBuZOTlBvc`&TVe_(pf;Uz} z<@tq@HJqn!j_m1eb6tKD6CRG+Kn1viSZ2H%Q;h)oQ0# zk>$1(<6W~(*_iqQ}Ed_?``kD2H5dFycn#7~e^vPeuf<(J~#iS3BL6K%E& zZvJ{T;JX>W=iT>KXmw3!PWtEXbs04QriicyGoxAzFbl&6sRmrZ&Z7!9O}enl?hwV) zZn{FLGHr)%x*q9v{&8@}Lf0_{L{wX={Sxlu>dlir)Y^$$F7mj8W1c5;vAR4Mh>n9X zGa5=JPU9K$Agt-d?%PDZz*&lyE-ISF6khV6%h!F#k0WBMkzq|B{2`HC+W9|v`1~%L z#lUe(U{exuSq+ztmWH;x>(t*WR&ibZG_^*+QKO}n_0DissY|cFRUmB1Ye$5UN=w=5 z7=y^^6^3LUH)__fUk&{JEjqL1@y9OmwA6;XM8Efs!-Mz4N*a)2A}@Y)wFQNpyG(xs zx|Ks9*oGc8^1f+83EzhAIa6)7#ixTYJ5IXY-FB-PsGxHcxZTgk0eaADz1t4_nbC~R zL5=BT?@r5)`ZS&c=JuYZugzjGe(vd7pz(FWw{tHkg5jPE*$h5M?j-&P23jI$8N|Z- zelV0WUjI!@IXo+YM(prH^N;6tFs>ksWa+AgdEp^NHH@$eUQ$o!7ayR*hd ztJG$1xu`lbrKFR#1ndXzGt{9AQh{D&o50tk3^s(){-9-1q)M!u&`}~EDVX~aiw_E znlDahSaR`Dwp@`g@V9JxKx6!9R6gb*N$_2(_i|%vW&F_r==rm!Uj|1% znpVR5oFp@Jz6h?`j6In|lpPhfRl{Xybol4(W!QEfPKEW}IEb2drV$RzeD&qe`JFr* z*J!?ZT7OC(L*s$FF=oyoIlik z$>)7z>9CZ@6E>7fQ;S+a+zfi7BH&TgF(w`aP4Zh*t)b(w9PDXOFYCF|EXTlIpveiJ zkXE%e3C?;%m*dH5=)3shk-H#v$WGEzW^)q5SZ1>053@-`V$W*o2yQ)gRM~NYz5Q7x zOpOt)*9_oI2WA_$57rRZ7bkO8cw(h)HUMc<@zx*Q;onn7@?9J6Uw-@ZXgq0fvHaUF zt8A#zFW+aH@SnUtb13^)DxUrR*T@1uzJ6xn5&7avf%3>HM#m48tRuzxZSe2h?|zH_ zFoKdm?g#X1A~Y6d@4*}GX=>CQIYpziTL<_8N*Rm6rfa;us2@#_Rnt`24$u* z?ByQ}@gK}zlope-Jj2VWv&Y%-b(xkdzi;+0W=S46PANklz3_Ybtz$&Y(Rgf>Xs$YR z9BWM0*<9{w?o(FKbvDgKI_I%wI;cKD`tyOKu1KPG1hr9xF#qCpuimxAGz)#uA`zIh z?{DMPl>-XLkA{Ah7|qo=_rZSuWEwV^pu~QrXe44KY4{kZ{bD1|ziOe26#AL-JM+`e zr$sI`t8Og(p-BzYO&vyWk{W$8xcRvqm9KvzilfR^a(G|9++7%pBzzy|X zUdP*x74O$-*Z!PJybTs6JXjO*dvwL8(y;iD=%t{?=UPCZ@3r#c6ET)ALjGGtOTVC~ zgc~tN=`MfX%KB?_KhjIP_a)EW9`WZ?BAMVKPF!=pH+IVFQy*5}SDCZNMPgu#{VY6; z>ug`;4rs4OG z`ovX}9$Y_swG+8!L-6d4uua#_*458s?qAunGdw$=oD`(Zvu*(p#6Dl)Vcm`}g&N&0 zet{2FV?-V%jr|2KZsCX<_yL7f{BJZN0${cqq>ddAAV`^=jvcrD2i{OJS1B~k;hiV) zjQxvQ+!VS&K~C$N*v5aUr#qGP*QWoym1oiYU$Bn!{k?EOP6#T?*{W~}@rr}f2jCx=thBlG+{^om zjDC$>*~1u$w@a;1XT{06*9n&tv#%4x15pUjU&C)V1Kn0latm_&1&IDaE)2*w&Q6@1 z<$uu{(ybUu)b^%Z4Ce--xkY7ecR&5baQ?zEw;PadFZ>ha8;Izi%}@VB7*cWV^E8gr zpqps?e-0ALg#QkF{oPZAvY4l^%m1K@lh3+V|L(1agXuf#=OS)XvcKg!b zcE|o{1byDmeD**{c5iufQiYAmW7X&}FWwtu54CNJ6L#Q)XUE-jnq@T@o%z^PU=RE6 z&}14vE_q`2e0|aLh31h7@=eu>h8{iozxTHPJU;#<{-k6d_VZ6mo3UGmWRxU*mnI9^ zvW3(;qvQ~2q-$v%L3l1gGwQ3oZ9(s3NI{NfxKwfuogfCel0vW~W-YdAi#k>#Hp_?? zNma6|02&F2wDYrJpD(|xhqHztX8C+;%cmC&e4?T?qQiqPk6U(q@69WB2!;%$B4_3* z&>tlr6;TAZF$z0a_X^8;Q#`~+%!AWK3A%lug_#fe=N3QG0%wMVHxuoc*$(zL(`4mD z)DFfk52lAFAocyIwkgst3AUx|VC62XZEgSR<7b*!Gb?A6g)n$z4UAEU$|NtL{Zq;g z8_wG1v8hQpIjvL$klNGCSmPFz>>W04jZx%6`YR=GH7i2jEFxMinHViKYw76=E{)YQ zW~BHC)%`Ch9Erm-a0JC`xgJ+iT))+4QMDW{YS0zfL$>7m{ZRbhrC;y}%L=f)gtaQC zABsXeWADz(=J0kjX;sM#*K0C4RfD~tg6!EQH}0d{`S}BB`BIx)RY=Qt-|UH0&T*(u zg;sl-jJZJNTpipX?adkMtZ1&Eyz)O``AK!Kcz#?2^%4>VYh&t9IIw#1DC(UBrU(rA*NF_UljG~0?Foa|3vW#YHJL)l-;kz zSJhGIY>6oqFVq-6ge=LkPJMuFx89pdDa#1P1Sg=?w;=Sp4V~KQUN_eQZ+8&+u6IV2>jV%<#U>BFILJ7=5Wow)BzhY<#dKPW=_8Pby!<+5ms--Q<0Xz1s{unp6BdjqO56A zC7I`Ot$x2{`A68ZKc6anu=6M~>iZ(W2`5HbGkBc?7`B3}HJ?o5@`saJnzT9#FX4MO zzP?bx^U%?T63!3_30T9%^O+POA8voE;GnJ$sTNcXpfKoleN9+R|;b&2Wp_9hQk6Pu;C6*;n|jx3cCJU zu#RK9-rHy)N8yb#kJ|TeOVY-+VHL;lV&9>_Pj*8~ zC7U%VeO#z*8Lxw$F|+`C(Q{_gZlnV=e|t5vP~md(2uFU?!i+$v%bg4IX@}i?&FVAZ z86Mocvv09|AIvHo@X4Ihh3zh8@|}lrq8*lO)@N|WdAYSXc~h87R`qX(-;+ilh5lXa z6UpQKR3^Dnv)C<9EO#ygaU8)IX|f8c1@u8x_y>^*@W<;m?MT9u!nVN=w?pBD8+6QU z-7nPsrM1RO7V@`;`$Rx<#@-y^Paoj&~PC?X6)JbvskX@2Mui5 zyO_sA=oX6%zVH-HJj=L(+nG>?3c5NgW9|geE*pLCw9-&j=_sIYQyqgjFOQ}W**@ap zNo!~H6+yp%c34o80D9D>;-z_)@KKA_zrHsB2X1a|Q@&&l?kQzzV!8UuU0)67n_KY8 zG=#&Oz-O>(_M}!bnd<1qk3jn9h$Dkic;S!~rPkkm(T`+7u0gHYI<+i6h?18L_Zf;m zcE^q2W|?>@omJ;o$gW@7M$*^{h~}JIV~%R9|Jb)u`)l|Bm3v;2#f&Vimwbq7c|Q1{ zojj@4w|%it-6iCRrgPS}<19e#mx~JXx(S0SuZJCP(chr0z;jwnx}TfSBst)iL8F~p z0cbn9=6lLjaje(D3wnybTm}u+E$|eRFpLe;|FQBIIf8FR0b#=Hu;5-$gX&It;jT(m zaacU=s0sy3LOme^PJR!Wuna@ z`yB7ifOrq@Zq4(6wd$Gt-96vcr*F)^7aV5E?!x2k|Lfp)c#ciVci&0U_`98R^A3Xp zW|WqK)Z4?Bws@(V{m8GM2({Z;D6Rg+{}7q%r5%yR^6d_OTcrZjE$zaxKZ6%nmx#dl zE^p)L!=^uOytU}4X$1k34mx==SpB!-dPCQtuProsI3gLfwwwnyDGD~+5FrI2E9zCA zs2Q74$bibl9oaukG51<+#I-_fK+>Qfy^kSg=E9vmpHKPoWuf9K#8 z=NAC#yi1z@qcLI;ai@D(1M$}*MmX!vJAC)vU2_U+b;;S7W^*SM(G@;;NGs@=Yf>L4 z6%r6@whj@fe<>y7qViOSKJC|?E94jjEovtDnt;StcjTP>(~GgXpe*5F9yOyFk*i1Y z?F1e3jYhx%i+(r8NS(MsKqD|hP!x3SZML^o%v&mSfh9PD%r6ZhXSkh?@_{e*gLO#r z)aQkr@!oIo8HddGt{kuH#(wU?oXe75t?R`cRWP+ZOE$gFD@;OQe8O(|#P_KO=YBDg z=MVl|z=t0b6^Gs+oXTK1W83#|;mfDT+uXIAsMj9+@d+4U4Y9*uml{xF1p0|^Xfq0y z;d17XsPH_lh!n+iW76_6Yc?EX{qNzW42%#+Qo!2KN<4NZLd|TOP z4A#DRY#yl$r#7xXKM*ELLwEfxeazYZ2;)#2-)}xqQ#BtorK+iG72&;ZwtXg+gVCAv zWA$o6q6~^#;`{OUDU{4s#jk*kG=uDBt;|#C0Xvl-ZQ%6skW)0Ib{7b9e(QeziGwUO zsiKgy{O&VkuEe91Wzn|dFB=b-PMIFU7Up?ksV?+dM+e2`+F`>V|e z9?eY%X##tn*Z|X>Ii>fY$N0FX70&Q(>Ni;)yY;3P8qG)syYc516+P^Wh?!4L8_=Oh z3(K2i5kbPcI(+uy7#Wv3QPfI>=Wp_qzfb7ZsL+Rzv$KlSZs}b(!L0Qqk+0oAoesnX z!+JYP{2bcH2>FyDEDFt8II=5Gu~7lvu3wXb0RMH@fegmsigW%e8->N^AI>`ea9@7B zx|*GSmf_X&d36c8TjyeWq5>U1?}1kSlPL8MBd%WmP0Mbg^Iwvse-hfitLiQaPf9*# z>A4U3B~DrKf-!%~xUa%DGz>W=1;g&&q=|o3-25i4RnaQgtk453u5VofL~mmi0f-p? zm-OsEFP>nved*+M|0Wasf5v^HrV*V~yOhM$sJ33QQklQfy=jR{?aurk&al~gtxw!tv4a8XI+e{) zU1V}DP|n_?7m4;W^|94WZuxi!uNxjUyBb#O#5}_opni}u*;C3h+ z7C%VzBkcY{&TuLS6iUN=c=E+N{_5P=a!)d9Kvq1jRB?k}z=7YW)Km1yrU?A!AHSzm zW2min{Fs<4$$7$(>Tyg8qGaksm_KN-*^UoL>E2*_(_jydFucbnlD_{1tpJySlsJV= zDX9c|IVdkF%yIwgRo1R^{YBye=9GDwSY;dUDfEB!a_!+vzwcir!YCGIqv#;aX&O3G zWI2~Z8%f3(V-!A4DX01nSx&>8%F0ZRi%BSwS`KqA$0(Y!s1YfL?@+(r_51Jl$M5=G z*K=L(XV0_i-ur%@`?;U{bzi$*fdRO{VfzOY%c_aQ^n$FeZH$Q+$DQIeScLlyi0v@g zk2h-~usp^Xc7Fhy_vCtGrVo;I?=G@PqU>S z2|wEXOx>`xoz<2*YiP8vu$WU0gB8B(44>}tlH;c9()>%Of~SR2O>hFeYQO&Z$Yzw@ zH^F-MGF2{#!nT&djIO2NpO6V-Pj|7{)l^b8wXLqa@!BUM7@?SLi}P{?eV@GQ+icfY z>Xch%&`QFxqfr=8WA|@NdqR!KwP%VV(VQmxTzI7`c1bZBu%)X)AEaBq>bSXu^o4hd zfj+{G`;WK$LPwbgmNkWl1#~w#dDe9`JN!JyQPP1BB!VyVq5i#+1-1h0ODd7gw4rFk2XA)Q*!b?`I862fDIhJYViUCRIbgf4&im}QO|Sm?dmWZJ+cQJ~ ze)whniSF53rqoJd1hXA}Yq(d#Co&t#;WcwMQ9>_iMT1o#V~h7M4uCl$!gM~(rtu7D5Ii; zWDE29pzGN(3w-HXyinr-8~4G+k;^CPH@e6PUFYh$zk8FqklPC@M__TT^R$*E6t*_M z2j4(HtxLLQU0;0|kgxI2RF7e{5Y$hF^&V_;sYo=qSCAl=ewx20jS*r_qyRcX)0lUX z(pHZTar~&N+ZJ2jOD^`|na)0_lYGyeW$6%|Ul^*RAhZj5BOQoUUE&(vIF}{>%`bDX zG($41iL8ssj&X>pg1_e{L?IMNEVTBQrkbxNgUVZ{EwTh24cn2QZ8&F@MT8TDXMb>O zDer%9sq`DF2DuzX+}shnc42$f z?6GYhg>!zvIU1$;Oaz+na)W4$Sat`+x|>~iJDz~ns@FY1Sc2Fdb*>WB&>25UpU8#F zV>V98TB#>co>@t*^$t8(>$U1sRy43n+CuSXd&sjm9_6`%$%{LRFZV6VOI=WU);Q0! z`DluBtnJGC*Zhd@_f*F(d=ew3r@I2E{}7yxov~=CUsoUshU!5V4#P`b#5#GX9*M2I z*k60*EfbSSBizgtH-rbd5S1o*a~1dKXlel}HA9==TP=^}sD zF?sr&l7_Ffx*+C7;6=3!9xbog3jYPmm_WWgh#6<*U>?i9Y+6oYqOSS6&9*d4Y^aLJl03fo9;e{Dqf^Ui|{uRCXV=FD8;lu)b+w6~8H#yyU9B-^ye-9R23lylT3)!Y zy51TV^gShihjwiGS?QQ zf$8H%v-5neXIc*T@}8`@`6A8mY195e~App zm)VCMu^lDJ^v4TYK31hm8!!Z;)Os5Q{u&J>KIgRl1K$uu$z`9MKn>xm0{6g9)ENRx z+C8(WTbT}q;e`*>S7#^6vMyFcfIuPl?q04t=c8r8a&NsU;^ zRlOU1I3+f5!L)pn5A^_^=6x3Vv6XMe{4#+4 ztc!fn8_1$4tp?eB64qcH)zca1xXv9wG8AGfOK#s~-ByR@`30hH0=OXNSk{ZrZMZWcu z?-5hvag>L|rih+SH*Aq?3g!JdUo9X3X}Ph=`ZDS&Sk*;N>LBZ0zhUs#3%es2(-Tc< zgO6-k^K2W9;-V^eJ4|)`?mUw&gh`cUJ3C$S<0c+eS3`V#C=g)aSVDKzIq>vE;!t-7 zGx?sZG1r}y!*7j{4*b0#R?KsLC`6~4nC9u5LX}-TSjLJqX=?;CO@{VPc0#|nh87c2 zJiG4*x}_eOp?lSEc2Q=>nU!w#_55q(to?qO+Bm)}Nt4YyI@x}Z9Ld3$-ZlG_>Lk|R ziVC=9*L2+p3830!IfL>$G{+Kb-nZ1srn|fYD_c z4sO6~r_0*zNY=xkJ%j z%@p3M6c0hYJ)uKw)2_ra+ftInv}$z7`g_y6DyNim)yrFU&NYiGN{dKt-5jzVq+cgv%_A4IS`?g zLd!~g=6vJZr<`vc1RdE(2j}eE-o^w_$?QO4&4TKyYVTvFQkoCxufDmcRW*iy_Z6&1 zuYVeTdAsq_+Z{5s%X|`7#5(m+VyLs%4=VDVyC{#|bH{7%oxR%7i$)aj&Nig57(oRH zv$Zok7#q8L7rC^*K+VC`FCW2-~6VOXn zt>m>C*TP+gfM|7Kb9~ced|Of{m^hWme8Wy&3C0t!2-Ox?F>^NT`6`p<^MM#M)J|;{ zu0wVgRxnv=t(n6y8Qq|UVA{JLWaYn?o^U4L=w~fdF@aaG$|39NJ_>nEcUG|%E0H_v zzVfnDTb2saixZ)eQdNFqOc?~cOe{Wv<`{D`Dpw*gW%M5Qub{BcwGwwIGgFILS@Adjs zH}q~)$igc-#xpR;cS8S4)6&%Q+DxvBNumyV>?h&(AlgQPzlML+y2Pz@m z5fRM-V&31chjMch4&lz*X10m%`kcAB!*P7V(triug}{4eDtgn zZc^RwJM!bP^dBxfC-6WX2!LD0!)@G(9rw68p8tPF*}p`#{}+t1;hq8f@zZyB!``+V QZSv|G8(N$z(Z@vo2U4MqkpKVy literal 0 HcmV?d00001 diff --git a/packages/preview/phonokit/0.5.19/gallery/ot_example.typ b/packages/preview/phonokit/0.5.19/gallery/ot_example.typ new file mode 100644 index 0000000000..00032a2442 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/gallery/ot_example.typ @@ -0,0 +1,17 @@ +#import "@preview/phonokit:0.5.19": * +#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.19/gallery/syllable_example.png b/packages/preview/phonokit/0.5.19/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.19/gallery/syllable_example.typ b/packages/preview/phonokit/0.5.19/gallery/syllable_example.typ new file mode 100644 index 0000000000..312baad77a --- /dev/null +++ b/packages/preview/phonokit/0.5.19/gallery/syllable_example.typ @@ -0,0 +1,6 @@ +#import "@preview/phonokit:0.5.19": * +#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.19/gallery/vowels_example.png b/packages/preview/phonokit/0.5.19/gallery/vowels_example.png new file mode 100644 index 0000000000000000000000000000000000000000..1c8404c48074c5d2854b015f1fb246b7c8dcdd73 GIT binary patch literal 24371 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 O?)>`cM_x`!4Fo~~gFwh# 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 N9%~wCyjQc0{2w 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": ( + // 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] + "\\nh": (root: ("+son", "-approx", "-vocoid"), coronal: true, anterior: "-", nasal: true, continuant: "-", segment: "\\nh"), + // N: nasal archiphoneme — deliberately unspecified for place + "\\N": (root: ("+son", "-approx", "-vocoid"), nasal: true, continuant: "-", 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: true, + 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 bézier curves that bow away +/// from the straight line between the two nodes instead of crossing it directly. +/// Use per-arrow `ctrl` offsets to adjust the curvature if needed. (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: bézier bowing away from the straight line between the nodes. + let head-back = 0.12 // canvas units of solid segment before the tip + + 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.19/grids.typ b/packages/preview/phonokit/0.5.19/grids.typ new file mode 100644 index 0000000000..0009963c28 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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.19/hasse.typ b/packages/preview/phonokit/0.5.19/hasse.typ new file mode 100644 index 0000000000..772f64eb45 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/hasse.typ @@ -0,0 +1,444 @@ +// 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 (no arrowheads: Hasse diagrams encode direction by + // vertical position, so edges are plain lines) + line((x1, y1), (x2, y2), stroke: stroke-style) + } + } + + // 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.19/intonational.typ b/packages/preview/phonokit/0.5.19/intonational.typ new file mode 100644 index 0000000000..60c04987c7 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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 a +/// superscript en dash. +/// +/// 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: 2em) +/// - lift (length): gap between text baseline and stem bottom (default: 0.8em) +/// - gap (length): gap between stem top and label bottom (default: 0.22em) +/// - en-dash (boolean): render phrase-accent hyphens as en dashes (default: true) +/// +/// 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.19/ipa.typ b/packages/preview/phonokit/0.5.19/ipa.typ new file mode 100644 index 0000000000..b0ba04c849 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/ipa.typ @@ -0,0 +1,422 @@ +// 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": "ʘ", + "\\textpipe": "ǀ", + "!": "ǃ", + "\\textdoublebarpipe": "ǂ", + "\\doublebarpipe": "ǂ", + "||": "ǁ", + "\\textdoublepipe": "ǁ", + // 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 (syllabic) + "\\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 if token in mappings { + // No backslash, but the whole token is a mapping (e.g., "||") + result += mappings.at(token) + if pending_diacritic != none { + result += pending_diacritic + pending_diacritic = none + } + } 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.19/lib.typ b/packages/preview/phonokit/0.5.19/lib.typ new file mode 100644 index 0000000000..3fc0d581c0 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/lib.typ @@ -0,0 +1,1170 @@ +// 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, ex-num-label, subex-label +#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.19": * +/// #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, 13), matching Parker's scale) +/// - 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: 18pt) +/// - 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 (array, optional): Per-level vertical spacing adjustments as (level, factor) tuples, e.g. `((0, 1.2),)` (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 (array, optional): Per-level vertical spacing adjustments as (level, factor) tuples, e.g. `((0, 1.2),)` (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 (array, optional): Per-level vertical spacing adjustments as (level, factor) tuples, e.g. `((0, 1.2),)` (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 (array, optional): Per-level vertical spacing adjustments as (level, factor) tuples, e.g. `((0, 1.2),)` (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 (array, optional): Per-level vertical spacing adjustments as (level, factor) tuples, e.g. `((0, 1.2),)` (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 (array, optional): Per-level vertical spacing adjustments as (level, factor) tuples, e.g. `((0, 1.2),)` (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)) +/// - circle-size (float): Multiplier for the radius of the highlight circles. +/// Arrows pointing at a highlighted vowel stop at its circle edge. Does not +/// affect the gaps cut in the grid lines behind vowels (default: 1) +/// - phoneme-colors (dictionary): Map vowel symbols to text colors. Keys accept +/// tipa-style notation and are matched after IPA conversion (default: (:)) +/// +/// 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 +/// - `#vowels("aeiou", phoneme-colors: ("i": red, "u": blue))` - Color selected phonemes +/// +/// 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) +/// - phoneme-colors (dictionary): Map consonant symbols to text colors. Keys +/// accept tipa-style notation and are matched after IPA conversion (default: (:)) +/// +/// 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 +/// - `#consonants("ptk", phoneme-colors: ("p": red, "t": blue))` - Color selected phonemes +/// +/// 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 (ratio): 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 + +/// Display the current example number inside an ex() body (legacy table mode). +/// +/// Use as the first-column cell of a table when managing numbering manually. +/// See also subex-label(). +#let ex-num-label = ex-num-label + +/// Display the next sub-example label (a., b., ...) inside an ex() body +/// (legacy table mode). Attach a label for cross-referencing, e.g. +/// `[#subex-label()]`. +#let subex-label = subex-label + +/// 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: ("-", "+")`. +/// - lateral (bool, str): `[lateral]`. Same sign convention as `nasal`. Attaches +/// under oral cavity (CH model) or under coronal (Sagey model). +/// - 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.19/multi-tier.typ b/packages/preview/phonokit/0.5.19/multi-tier.typ new file mode 100644 index 0000000000..f5b7c095dd --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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 6: 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 7: 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 8: 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 9: 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.19/ot.typ b/packages/preview/phonokit/0.5.19/ot.typ new file mode 100644 index 0000000000..beccf48c8e --- /dev/null +++ b/packages/preview/phonokit/0.5.19/ot.typ @@ -0,0 +1,1262 @@ +#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.19/phonetics.typ b/packages/preview/phonokit/0.5.19/phonetics.typ new file mode 100644 index 0000000000..370e191d5f --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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: [DEV], + ), + "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.19/prosody.typ b/packages/preview/phonokit/0.5.19/prosody.typ new file mode 100644 index 0000000000..525f148af2 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/prosody.typ @@ -0,0 +1,2212 @@ +#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 + // Covers the full IPA vowel inventory (see vowels.typ), plus rhotacized ɚ/ɝ + let base-is-vowel = ( + base + in ( + "a", "e", "i", "o", "u", "y", "ɚ", "ɝ", "ɯ", "ɐ", "ɒ", "æ", "ɛ", "ɪ", "ɔ", "ø", "œ", "ɨ", "ʉ", "ʊ", "ə", "ʌ", + "ɑ", "ʏ", "ɘ", "ɵ", "ɤ", "ɜ", "ɞ", "ɶ", + ) + ) + + // 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 + break + } + } + + 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 + break + } + } + 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 + break + } + } + 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 + break + } + } + + 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 + break + } + } + + 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 + break + } + } + 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.19/sonority.typ b/packages/preview/phonokit/0.5.19/sonority.typ new file mode 100644 index 0000000000..9e01b2163d --- /dev/null +++ b/packages/preview/phonokit/0.5.19/sonority.typ @@ -0,0 +1,247 @@ +// 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, + "ɒ": 13, + "ɶ": 13, + "ɛ": 12, + "ɔ": 12, + "ʌ": 12, + "œ": 12, + "ɜ": 12, + "ɞ": 12, + "e": 12, + "o": 12, + "ɤ": 12, + "ə": 12, + "ɘ": 12, + "ɵ": 12, + "ø": 12, + "ɚ": 12, + "ɝ": 12, + "i": 12, + "u": 12, + "ɪ": 12, + "ʊ": 12, + "y": 12, + "ʏ": 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, 13), // Sonority range for y-axis (Parker's scale maxes at 13) + 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 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.19/sound-shift.typ b/packages/preview/phonokit/0.5.19/sound-shift.typ new file mode 100644 index 0000000000..a1e6229631 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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.19/typst.toml b/packages/preview/phonokit/0.5.19/typst.toml new file mode 100644 index 0000000000..0f70ecdb3b --- /dev/null +++ b/packages/preview/phonokit/0.5.19/typst.toml @@ -0,0 +1,23 @@ +[package] +categories = ["utility", "text", "visualization"] +disciplines = ["linguistics"] +name = "phonokit" +version = "0.5.19" +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.19/ui-lang.typ b/packages/preview/phonokit/0.5.19/ui-lang.typ new file mode 100644 index 0000000000..3b33162fa7 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/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", + "Rétroflexe", + "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": "gl ab", + "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.19/vowels.typ b/packages/preview/phonokit/0.5.19/vowels.typ new file mode 100644 index 0000000000..e5be9c7a13 --- /dev/null +++ b/packages/preview/phonokit/0.5.19/vowels.typ @@ -0,0 +1,605 @@ +#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.replace(regex("\\s+"), " ")) + let oral = "" + let nasals = () + let rejected = () + + 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) + } + } else if cluster not in rejected { + rejected.push(cluster) + } + } + + (oral: oral, nasals: nasals, rejected: rejected) +} + +// 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) + circle-size: 1, // Multiplier for the radius of highlight circles + phoneme-colors: (:), // Dictionary of phoneme-specific text colors +) = { + // 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) + // An unfinished inventory should stay quiet while the user is typing. + if lang == none and (vowel-string == none or vowel-string.trim() == "") { + return none + } + + // Determine which vowels to plot + let vowels-to-plot = "" + let nasal-target-vowels = () + let error-msg = none + let inventory-error = [ + *Error:* Available presets: #(language-vowels.keys().map(raw).join(", ")). + Use a listed preset or provide only vowel symbols. + ] + + // 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 + return inventory-error + } + } 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) + if parsed.rejected.len() > 0 { + return inventory-error + } + if parsed.oral == "" { + return inventory-error + } + vowels-to-plot = parsed.oral + nasal-target-vowels = parsed.nasals + } else { + // Nothing specified + error-msg = inventory-error + } + + // 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 + // White mask circles: fixed size, only cut gaps in the grid lines behind vowels + let scaled-circle-radius = 0.35 * scale + // Colored highlight circles: drawn above the masks, resizable via circle-size + let scaled-highlight-radius = 0.4 * circle-size * 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))) + let resolved-phoneme-colors = (:) + for (phoneme, color) in phoneme-colors { + resolved-phoneme-colors.insert(ipa-to-unicode(phoneme), color) + } + let vowel-text = (size, phoneme) => { + if phoneme in resolved-phoneme-colors { + text(size: size, font: phonokit-font.get(), fill: resolved-phoneme-colors.at(phoneme), top-edge: "x-height", bottom-edge: "baseline", phoneme) + } else { + text(size: size, font: phonokit-font.get(), top-edge: "x-height", bottom-edge: "baseline", phoneme) + } + } + let shifted-vowel-text = (size, phoneme) => { + text(size: size, font: phonokit-font.get(), fill: resolved-phoneme-colors.at(phoneme, default: shift-color), top-edge: "x-height", bottom-edge: "baseline", phoneme) + } + + 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)) + } + } + + // Radius at which an arrow pointing at `endpoint` should stop: the edge of + // the highlight circle if that vowel is highlighted, else the mask edge. + let end-radius(endpoint) = { + let highlighted = if type(endpoint) == str { + ipa-to-unicode(endpoint) in highlight-set + } else { + let v = ipa-to-unicode(endpoint.at(0)) + highlight-shifts.any(h => h.at(0) == v and h.at(1) == endpoint.at(1) and h.at(2) == endpoint.at(2)) + } + if highlighted { calc.max(scaled-circle-radius, scaled-highlight-radius) } else { scaled-circle-radius } + } + + // 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 end-r = end-radius(arrow.at(1)) + let adjusted-to = ( + to-pos.at(0) - tangent.at(0) * end-r, + to-pos.at(1) - tangent.at(1) * end-r, + ) + + arrows-data.push(( + from-pos: from-pos, + to-pos: to-pos, + ctrl: ctrl, + tangent: tangent, + adjusted-to: adjusted-to, + end-r: end-r, + )) + } + } + + // ── 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) * a.end-r, + a.to-pos.at(1) - avg-tan.at(1) * a.end-r, + ) + (from-pos: a.from-pos, to-pos: a.to-pos, ctrl: a.ctrl, + tangent: avg-tan, adjusted-to: snapped, end-r: a.end-r) + } 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) + } + } + } + + // White mask circles: cut a gap in the grid lines behind every vowel + for vp in vowel-positions { + circle(vp.pos, radius: scaled-circle-radius, fill: white, stroke: none) + } + + // Highlight circles: above the masks, below the minimal-pair bullets and symbols + for vp in vowel-positions.filter(vp => vp.vowel in highlight-set) { + circle(vp.pos, radius: scaled-highlight-radius, fill: highlight-color, stroke: none) + } + + // 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 vowel symbols + for vp in vowel-positions { + content(vp.pos, context vowel-text(scaled-font-size * 1pt, 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) + circle(shifted-pos, radius: scaled-circle-radius, fill: white, stroke: none) + if highlight-shifts.any(h => h.at(0) == vowel and h.at(1) == x-off and h.at(2) == y-off) { + circle(shifted-pos, radius: scaled-highlight-radius, fill: highlight-color, stroke: none) + } + content(shifted-pos, context shifted-vowel-text(resolved-shift-size, vowel)) + } + } + }) +} From ad822c305612e93aea0c6f5776321ac06bfdebcb Mon Sep 17 00:00:00 2001 From: "Guilherme D. Garcia" Date: Tue, 6 Oct 2026 23:23:46 -0400 Subject: [PATCH 2/3] =?UTF-8?q?phonokit:0.5.19=20=E2=80=93=20scale=20sonor?= =?UTF-8?q?ity()=20labels=20and=20strokes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/preview/phonokit/0.5.19/sonority.typ | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/preview/phonokit/0.5.19/sonority.typ b/packages/preview/phonokit/0.5.19/sonority.typ index 9e01b2163d..9cb83fcd62 100644 --- a/packages/preview/phonokit/0.5.19/sonority.typ +++ b/packages/preview/phonokit/0.5.19/sonority.typ @@ -195,7 +195,10 @@ v(0.5em) } - cetz.canvas(length: scale * 1cm, { + // Bind locally: inside the canvas closure `scale` resolves to Typst's built-in + let scale-factor = scale + + cetz.canvas(length: scale-factor * 1cm, { import cetz.draw: * set-origin((0, 0)) @@ -208,7 +211,7 @@ 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")) + line((x1, y1), (x2, y2), stroke: (thickness: 0.5pt * scale-factor, paint: gray, dash: "dashed")) } } @@ -233,13 +236,13 @@ (x - box-size / 2, y - box-size / 2), (x + box-size / 2, y + box-size / 2), fill: box-fill, - stroke: 0.5pt + black, + stroke: (0.5pt * scale-factor) + black, ) // Add phoneme label (always black now) content( (x, y), - context text(size: 10pt, font: phonokit-font.get(), fill: black)[#phoneme], + context text(size: 10pt * scale-factor, font: phonokit-font.get(), fill: black)[#phoneme], anchor: "center", ) } From 20cc09be70e65ae04aaf7255abc67b599909f7dc Mon Sep 17 00:00:00 2001 From: "Guilherme D. Garcia" Date: Wed, 7 Oct 2026 18:47:23 -0400 Subject: [PATCH 3/3] phonokit:0.5.19 --- packages/preview/phonokit/0.5.19/prosody.typ | 27 +++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/preview/phonokit/0.5.19/prosody.typ b/packages/preview/phonokit/0.5.19/prosody.typ index 525f148af2..252d5efa99 100644 --- a/packages/preview/phonokit/0.5.19/prosody.typ +++ b/packages/preview/phonokit/0.5.19/prosody.typ @@ -1401,7 +1401,18 @@ } 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 } + // If the user marked stress with ' (even without using feet), align + // the PWd node to that syllable instead of defaulting to L/R extremes. + let stressed-idx = none + for (idx, syll) in syllables.enumerate() { + if syll.stressed { + stressed-idx = idx + break + } + } + let target-idx = if stressed-idx != none { + stressed-idx + } else if foot == "L" { 0 } else { syllables.len() - 1 } pwd-x = start-x + syllable-positions.at(target-idx) } @@ -2065,6 +2076,20 @@ } } pwd-x = start-x + syllable-positions.at(target-syll-idx) + } else if syllables.len() > 0 { + // If the user marked stress with ' (even without using feet), align + // the PWd node to that syllable instead of defaulting to L/R extremes. + let stressed-idx = none + for (idx, syll) in syllables.enumerate() { + if syll.stressed { + stressed-idx = idx + break + } + } + let target-idx = if stressed-idx != none { + stressed-idx + } else if foot == "L" { 0 } else { syllables.len() - 1 } + pwd-x = start-x + syllable-positions.at(target-idx) } // Vertical level positions