@austencloud/tka-pictograph
v0.2.1
Published
Draws The Kinetic Alphabet's pictographs as SVG, in Node or a browser, from Austen Cloud's own renderer and art.
Readme
@austencloud/tka-pictograph
Draws The Kinetic Alphabet's pictographs as SVG: the grid, props, arrows, turn numbers and letter, from Austen Cloud's own renderer and art. The same renderer makes the pictographs in the TKA knowledge server. It runs in a browser, a desktop webview or Node, and works offline.
Use
import { pictographsForLetter, renderPictographSvg } from "@austencloud/tka-pictograph";
const [a] = pictographsForLetter("A", { start: "alpha" });
const svg = await renderPictographSvg(a, { darkMode: true, leftPropType: "staff", rightPropType: "staff" });renderPictographSvg returns an SVG document 950 units square (PICTOGRAPH_VIEWBOX). Put it in the page as markup or as an image source.
A pictograph is a PictographInput: the letter, the start and end placements, the grid mode, and one MotionInput per hand (motion type, rotation direction, start and end location, turns and start orientation). End orientations are worked out when they are left out. pictographsForLetter returns every version of a letter from the TKA dataframe with no turns and both props starting in; start keeps those that start at one placement ("alpha3") or a group ("alpha").
handColor(hand, darkMode) gives the colour each hand is drawn in, so the rest of an app can match.
Laying TKA out
These are the canonical layouts, the ones the Flow Arts Composer, the Codex and the guide use. An app that shows TKA follows them rather than wrapping pictographs or letters to whatever fits, so the alphabet looks the same wherever it appears.
- Letters.
LETTER_ROWSis the alphabet in the Codex's rows: Dual-Shift six to a row (A-F, G-L, M-R, S-V), Shift and Cross-Shift four (W-Z, Σ Δ Θ Ω, and their dash forms), Dash, Dual-Dash and Static three. A letter picker draws these rows as they are, on a gridLETTER_ROW_COLUMNS(6) wide, each row under its type's name in TKA's colours (getColoredTextParts).renderLetterSvg(letter)draws a key's letter, dash included.alphabetSections()groups the same letters by type. - A sequence. The start position takes column 1 of the first row. The beats fill the columns beside it in the rows
getStepFrameLayout(beats, wide)gives: 16 beats are 4 by 4 in the standard table and 2 rows of 8 in the wide one. Use the wide table where width is plenty and height is short, such as a strip under a stage.calculateStepPosition(i, columns)places beati. The count is the sequence's beats, so the shape of the grid shows the shape of the sequence: a quarter-turn loop of 16 reads as four rows of four. - A sequence that comes round more than once. When a sequence only closes after playing it again (rotated, mirrored or swapped), show the beats it plays to come round as well, in the same grid and counted in its layout, drawn apart from the written ones (faded, dashed edge, not pickable) so it is plain they follow from the written beats and change when those do.
- Reversals. A hand reverses on a beat where its prop spins the other way from the last beat it spun in, and each hand is judged apart.
markReversals(beats, { loop })sets each beat'sleftReversalandrightReversalby the Composer's rule (a looping sequence reads round its seam), andrenderPictographSvg(beat, { showReversals: true })draws them as the Composer does: a dot in the hand's colour at the left edge, red above blue when both reverse. Show reversals this way, never as one mark for the whole beat that cannot say which hand turned. - The letters that can come next.
sectionsByLetterTypegives one section per type incompareOptionLettersorder. Each type takes a row ofoptionColumns(width)(eight on a wide picker, four otherwise), and Types 4, 5 and 6 share the last row (type456Columns). - Changing a whole sequence.
transformBeats(beats, change)mirrors, flips, rotates an eighth (onto the other grid), swaps hands, inverts or rewinds, and reletters each beat from the dataframe, as the Composer's sequence tools do. - Elements.
ELEMENTSholds the six TKA elements, one per timing and direction between the hands (Earth together-same, Air together-opposite, Water split-same, Fire split-opposite, Sun quarter-same, Moon quarter-opposite), each with its colour and the path of its icon in the art (images/elements/norm/water.webp, the app's icon with its code, SS, drawn in).elementOf(beat)reads a beat's element from the hands' paths round the grid, so it works on the diamond and the box; a static or dash hand has none.renderPictographSvg(beat, { showElemental: true })draws the hands' element in the bottom-right corner, as the TKA app does. Show an element with its icon and colour, never its timing and direction alone. - Playing a sequence. While a sequence plays, exactly one tile is lit: the beat being played. When playback reaches a beat the sequence plays to come round, the copy lights in place of the written beat it repeats, never alongside it, so the eye follows one tile at a time.
Generate
A second entry makes sequences the way the Composer's Generate tab does, with the same engine. It is a separate bundle (about 690 KB, 83 KB gzipped, with the diamond and box dataframes and the letter mappings inside), so an app that only draws never loads it.
import { generateSequence } from "@austencloud/tka-pictograph/generate";
import { renderPictographSvg } from "@austencloud/tka-pictograph";
const result = generateSequence({ length: 16, level: 2, loop: { kind: "rotated" }, seed: 42 });
if (result.ok) {
const svg = await renderPictographSvg(result.beats[0]);
} else {
console.log(result.why, result.suggestions);
}Nothing here throws for a request that cannot be met. It returns { ok: false, why, suggestions }, with why a sentence a person can read and suggestions what would make it work, when that is known.
generateSequence(options)returns{ ok: true, start, beats, bridges, word, loop }.startis the start position andbeatsthe beats, each aPictographInputwith turns and orientations filled, ready forrenderPictographSvg.bridgeslists the beats added to join two letters of a word.wordis the sequence's letters.loop, for a LOOP, gives itskind, itsperiod, theseedWordthat repeats, thederivedWordit adds to come round, andderived, the beats it added.minimumLength(options)is the fewest beats the options allow, such as 8 for a quartered rotated LOOP at level 2.nextLetterOptions(beats, { grid })is every pictograph that can follow the last beat: those that start where it ends, carrying its orientations on. It is the rule the Composer's option picker uses.wordFeasibility(word, { grid, handPath, propSpin })reads a word into letters and says whether it can keep the hand path and prop spin asked for (fits), with the fewest and most reversals it needs or allows.
The options, all optional except a length or a word:
length: how many beats, not counting the start position.word: a word to spell, such as"ABC","AΣ-B"or"A sigma B". With a LOOP, the word is what repeats. Letters are joined with bridge letters where they do not meet.level: 1 has no turns, 2 adds whole turns, 3 half turns too, 4 everything. Defaults to 1.turnIntensity: the most turns a prop takes in one beat.matchHandTurns: give both hands the same turns on every beat.turnPattern: turns to repeat instead of choosing them, per hand, such as{ left: [0, 1], right: [0.5] }."fl"is a float.grid:"diamond"(the default) or"box". The skewed grid is not available yet and returnsok: false.handPath: how often the hands' paths turn back:"smooth"(never, the default),"mixed"or"choppy"(every beat).propSpin: the same for the props' spin.dashes: dash beats"allow"ed (the default),"avoid"ed or"prefer"red.handRelationship: timing and direction between the hands:"free"(the default), orT,SorQ(together, split, quarter) followed bySorO(same, opposite), such as"SO".propRelationship: the same between the props' spins.start: the placement to start from, such as"alpha1".blockedStarts: placements never to start from.ends: placements the last beat may end at. Any, when empty.mustContainandmustNotContain: letters that must or must not appear.startOrientation: which way each prop faces at the start,{ left, right }, each"in","out","clock"or"counter". Defaults to in.allowStatic: allow static letters as beats when they carry turns. Defaults to true.layerPattern: which layers the props pass through, written like"1:.XB.". Needs level 3.loop: make the sequence a LOOP:kind: what it does each time it comes round:"rotated","mirrored","flipped","swapped","inverted"or"rewound", or a combination such as"mirrored_swapped"or["mirrored", "swapped"].period:"halved"comes round twice (the default),"quartered"four times. Only rotation quarters.inversionInterval: how many repeats an inversion takes to come back, 2 or 4.inversionMode:"expand"adds the inverted repeats;"overlay"inverts repeats in place.reflectionAxis: the axis a mirrored LOOP reflects across.spec: a full per-hand spec, used as given in place of the settings above.
seed: a number. The same seed and options give the same sequence.
Art
The art ships in the package's assets/ folder, about 5 MB, and loads only when a pictograph needs it.
Default: with no
artoption, files are fetched fromassets/next to the package'sdist/. This works in development and in Node.A production web build: copy
node_modules/@austencloud/tka-pictograph/assets/into your public folder and pass its URL:import { urlArtSource } from "@austencloud/tka-pictograph"; const art = urlArtSource("/tka-pictograph/"); await renderPictographSvg(input, { art });Reuse one source; it caches what it has fetched.
Node, from disk:
const art = await fsArtSource(pathToAssets).Your own source: any object with
read(path): Promise<string | null>, returningnullfor a file that does not exist.
License
The code (dist/) and the placement data (assets/data/) are Elastic License 2.0; see LICENSE. Austen Cloud's MIT-licensed render-core calculations are bundled in dist/.
The pictograph art (assets/images/) is CC BY 4.0; see LICENSE-ART.md. Keep the credit "The Kinetic Alphabet by Austen Cloud" with a link to https://tkaflowarts.com wherever the pictographs appear ("TKA by Austen Cloud" with the TKA logo where space is tight). The names and the logo are Austen Cloud's marks, usable for that credit and nothing else.
