magick-components 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE.txt +89 -0
  2. package/README.md +353 -2
  3. package/fonts/FrankRuhlLibre-Bold-letters.woff2 +0 -0
  4. package/fonts/FrankRuhlLibre-OFL.txt +105 -0
  5. package/fonts/NotoSerif-BoldItalic-compass.woff2 +0 -0
  6. package/fonts/NotoSerif-Italic-latin1.woff2 +0 -0
  7. package/fonts/NotoSerif-OFL.txt +93 -0
  8. package/fonts/SOURCES.md +35 -0
  9. package/fonts/gd-hall.css +3 -0
  10. package/lib/astrology/Kamea.d.ts +75 -0
  11. package/lib/astrology/Kamea.js +73 -0
  12. package/lib/astrology/MercuryWidget.d.ts +21 -0
  13. package/lib/astrology/MercuryWidget.js +80 -0
  14. package/lib/astrology/Moon.d.ts +32 -0
  15. package/lib/astrology/Moon.js +39 -0
  16. package/lib/astrology/MoonWidget.d.ts +20 -0
  17. package/lib/astrology/MoonWidget.js +63 -0
  18. package/lib/astrology/PlanetarySpirit.d.ts +103 -0
  19. package/lib/astrology/PlanetarySpirit.js +102 -0
  20. package/lib/astrology/mercuryRetrograde.d.ts +20 -0
  21. package/lib/astrology/mercuryRetrograde.js +80 -0
  22. package/lib/astrology/moonPath.d.ts +20 -0
  23. package/lib/astrology/moonPath.js +45 -0
  24. package/lib/chakras/Chakras.d.ts +34 -0
  25. package/lib/chakras/Chakras.js +149 -0
  26. package/lib/cssString.d.ts +5 -0
  27. package/lib/cssString.js +7 -0
  28. package/lib/drawingVersions.d.ts +29 -0
  29. package/lib/drawingVersions.js +15 -0
  30. package/lib/enochian/Tablet.d.ts +17 -0
  31. package/lib/enochian/Tablet.js +24 -0
  32. package/lib/enochian/tabletSigils.d.ts +46 -0
  33. package/lib/enochian/tabletSigils.js +172 -0
  34. package/lib/enochian/tabletSvg.d.ts +85 -0
  35. package/lib/enochian/tabletSvg.js +278 -0
  36. package/lib/gd/FylfotCross.d.ts +60 -0
  37. package/lib/gd/FylfotCross.js +121 -0
  38. package/lib/gd/GradeTree.d.ts +10 -0
  39. package/lib/gd/GradeTree.js +21 -0
  40. package/lib/gd/Lamen.d.ts +31 -0
  41. package/lib/gd/Lamen.js +317 -0
  42. package/lib/gd/RoseSigil.d.ts +31 -0
  43. package/lib/gd/RoseSigil.js +49 -0
  44. package/lib/gd/RoseSigilImage.d.ts +38 -0
  45. package/lib/gd/RoseSigilImage.js +29 -0
  46. package/lib/gd/SevenBranchedCandlestick.d.ts +54 -0
  47. package/lib/gd/SevenBranchedCandlestick.js +269 -0
  48. package/lib/gd/TableOfShewbread.d.ts +19 -0
  49. package/lib/gd/TableOfShewbread.js +120 -0
  50. package/lib/gd/hall/GdHall.d.ts +94 -0
  51. package/lib/gd/hall/GdHall.js +147 -0
  52. package/lib/gd/hall/art/altar.d.ts +8 -0
  53. package/lib/gd/hall/art/altar.js +21 -0
  54. package/lib/gd/hall/art/altarItems.d.ts +20 -0
  55. package/lib/gd/hall/art/altarItems.js +113 -0
  56. package/lib/gd/hall/art/banner.d.ts +23 -0
  57. package/lib/gd/hall/art/banner.js +64 -0
  58. package/lib/gd/hall/art/board.d.ts +9 -0
  59. package/lib/gd/hall/art/board.js +49 -0
  60. package/lib/gd/hall/art/cards.d.ts +11 -0
  61. package/lib/gd/hall/art/cards.js +124 -0
  62. package/lib/gd/hall/art/furnishings.d.ts +12 -0
  63. package/lib/gd/hall/art/furnishings.js +36 -0
  64. package/lib/gd/hall/art/geometry.d.ts +49 -0
  65. package/lib/gd/hall/art/geometry.js +97 -0
  66. package/lib/gd/hall/art/hebrewLetter.d.ts +10 -0
  67. package/lib/gd/hall/art/hebrewLetter.js +38 -0
  68. package/lib/gd/hall/art/index.d.ts +7 -0
  69. package/lib/gd/hall/art/index.js +61 -0
  70. package/lib/gd/hall/art/items.d.ts +12 -0
  71. package/lib/gd/hall/art/items.js +125 -0
  72. package/lib/gd/hall/art/kamea.d.ts +20 -0
  73. package/lib/gd/hall/art/kamea.js +64 -0
  74. package/lib/gd/hall/art/lamen.d.ts +9 -0
  75. package/lib/gd/hall/art/lamen.js +52 -0
  76. package/lib/gd/hall/art/lightning.d.ts +13 -0
  77. package/lib/gd/hall/art/lightning.js +44 -0
  78. package/lib/gd/hall/art/lights.d.ts +13 -0
  79. package/lib/gd/hall/art/lights.js +64 -0
  80. package/lib/gd/hall/art/marker.d.ts +8 -0
  81. package/lib/gd/hall/art/marker.js +19 -0
  82. package/lib/gd/hall/art/pantacle.d.ts +10 -0
  83. package/lib/gd/hall/art/pantacle.js +119 -0
  84. package/lib/gd/hall/art/pillar.d.ts +4 -0
  85. package/lib/gd/hall/art/pillar.js +25 -0
  86. package/lib/gd/hall/art/placeholder.d.ts +8 -0
  87. package/lib/gd/hall/art/placeholder.js +18 -0
  88. package/lib/gd/hall/art/platform.d.ts +13 -0
  89. package/lib/gd/hall/art/platform.js +28 -0
  90. package/lib/gd/hall/art/seals.d.ts +23 -0
  91. package/lib/gd/hall/art/seals.js +151 -0
  92. package/lib/gd/hall/art/shared.d.ts +87 -0
  93. package/lib/gd/hall/art/shared.js +95 -0
  94. package/lib/gd/hall/art/symbols.d.ts +37 -0
  95. package/lib/gd/hall/art/symbols.js +134 -0
  96. package/lib/gd/hall/art/tarotKey.d.ts +34 -0
  97. package/lib/gd/hall/art/tarotKey.js +101 -0
  98. package/lib/gd/hall/art/veil.d.ts +8 -0
  99. package/lib/gd/hall/art/veil.js +22 -0
  100. package/lib/gd/hall/art/vessels.d.ts +19 -0
  101. package/lib/gd/hall/art/vessels.js +74 -0
  102. package/lib/gd/hall/aspect.d.ts +31 -0
  103. package/lib/gd/hall/aspect.js +161 -0
  104. package/lib/gd/hall/attributes.d.ts +52 -0
  105. package/lib/gd/hall/attributes.js +116 -0
  106. package/lib/gd/hall/describe.d.ts +38 -0
  107. package/lib/gd/hall/describe.js +184 -0
  108. package/lib/gd/hall/dimensions.d.ts +39 -0
  109. package/lib/gd/hall/dimensions.js +49 -0
  110. package/lib/gd/hall/editor.d.ts +13 -0
  111. package/lib/gd/hall/editor.js +13 -0
  112. package/lib/gd/hall/fontMetrics.json +177 -0
  113. package/lib/gd/hall/fonts.d.ts +45 -0
  114. package/lib/gd/hall/fonts.js +73 -0
  115. package/lib/gd/hall/hebrewLetters.d.ts +8 -0
  116. package/lib/gd/hall/hebrewLetters.js +40 -0
  117. package/lib/gd/hall/index.d.ts +19 -0
  118. package/lib/gd/hall/index.js +17 -0
  119. package/lib/gd/hall/labels.d.ts +72 -0
  120. package/lib/gd/hall/labels.js +109 -0
  121. package/lib/gd/hall/layout.d.ts +213 -0
  122. package/lib/gd/hall/layout.js +837 -0
  123. package/lib/gd/hall/planets.d.ts +13 -0
  124. package/lib/gd/hall/planets.js +35 -0
  125. package/lib/gd/hall/presets/index.d.ts +13 -0
  126. package/lib/gd/hall/presets/index.js +29 -0
  127. package/lib/gd/hall/presets/neophyte.d.ts +7 -0
  128. package/lib/gd/hall/presets/neophyte.js +74 -0
  129. package/lib/gd/hall/presets/philosophus.d.ts +30 -0
  130. package/lib/gd/hall/presets/philosophus.js +244 -0
  131. package/lib/gd/hall/presets/portal.d.ts +24 -0
  132. package/lib/gd/hall/presets/portal.js +241 -0
  133. package/lib/gd/hall/presets/practicus.d.ts +17 -0
  134. package/lib/gd/hall/presets/practicus.js +181 -0
  135. package/lib/gd/hall/presets/theoricus.d.ts +13 -0
  136. package/lib/gd/hall/presets/theoricus.js +178 -0
  137. package/lib/gd/hall/presets/types.d.ts +72 -0
  138. package/lib/gd/hall/presets/types.js +58 -0
  139. package/lib/gd/hall/presets/zelator.d.ts +12 -0
  140. package/lib/gd/hall/presets/zelator.js +161 -0
  141. package/lib/gd/hall/spec.d.ts +54 -0
  142. package/lib/gd/hall/spec.js +108 -0
  143. package/lib/gd/hall/types.d.ts +131 -0
  144. package/lib/gd/hall/types.js +1 -0
  145. package/lib/gd/hall/validate.d.ts +13 -0
  146. package/lib/gd/hall/validate.js +285 -0
  147. package/lib/gd/hall/vocabulary.d.ts +139 -0
  148. package/lib/gd/hall/vocabulary.js +435 -0
  149. package/lib/gd/lamenGlyphs.d.ts +16 -0
  150. package/lib/gd/lamenGlyphs.js +167 -0
  151. package/lib/gd/lamenGrades.d.ts +29 -0
  152. package/lib/gd/lamenGrades.js +36 -0
  153. package/lib/gd/roseSigilGeometry.d.ts +10 -0
  154. package/lib/gd/roseSigilGeometry.js +10 -0
  155. package/lib/gd/roseSigilInternals.d.ts +85 -0
  156. package/lib/gd/roseSigilInternals.js +343 -0
  157. package/lib/gd/roseSigilOptimizer.d.ts +15 -0
  158. package/lib/gd/roseSigilOptimizer.js +59 -0
  159. package/lib/gd/roseSigilVersions.d.ts +12 -0
  160. package/lib/gd/roseSigilVersions.js +57 -0
  161. package/lib/hooks/useHydrated.d.ts +9 -0
  162. package/lib/hooks/useHydrated.js +13 -0
  163. package/lib/kabbalah/TreeOfLife.d.ts +135 -0
  164. package/lib/kabbalah/TreeOfLife.js +312 -0
  165. package/lib/kabbalah/fieldPath.d.ts +25 -0
  166. package/lib/kabbalah/fieldPath.js +100 -0
  167. package/lib/kabbalah/treeLabels.d.ts +30 -0
  168. package/lib/kabbalah/treeLabels.js +32 -0
  169. package/lib/kabbalah/treeOfLifeFigure.d.ts +59 -0
  170. package/lib/kabbalah/treeOfLifeFigure.js +151 -0
  171. package/lib/svgCoordinate.d.ts +12 -0
  172. package/lib/svgCoordinate.js +16 -0
  173. package/package.json +162 -3
@@ -0,0 +1,121 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import elements from "magick-data/alchemy/elements";
3
+ import planets from "magick-data/astrology/planets";
4
+ import zodiac from "magick-data/astrology/zodiac";
5
+ const colorFromElement = {
6
+ fire: "red",
7
+ water: "blue",
8
+ air: "yellow",
9
+ earth: "#333",
10
+ };
11
+ const flashingColorFromColor = {
12
+ red: "green",
13
+ blue: "yellow",
14
+ yellow: "purple",
15
+ "#333": "red",
16
+ };
17
+ function elementFromPos(row, col) {
18
+ if (row < 2)
19
+ return col < 3 ? "fire" : "water";
20
+ if (row === 2) {
21
+ if (col === 2)
22
+ return "sol";
23
+ return col < 2 ? "earth" : "water";
24
+ }
25
+ if (row > 2)
26
+ return col < 2 ? "earth" : "air";
27
+ return "invalid-position";
28
+ }
29
+ /**
30
+ * The finished cross, row by row: each square's symbol by its id in
31
+ * magick-data, and `null` where the cross has no square.
32
+ */
33
+ export const FYLFOT_CELLS = [
34
+ ["fire", "sagittarius", "leo", null, "water"],
35
+ [null, null, "aries", null, "pisces"],
36
+ ["taurus", "capricorn", "sol", "cancer", "scorpio"],
37
+ ["virgo", null, "libra", null, null],
38
+ ["earth", null, "aquarius", "gemini", "air"],
39
+ ];
40
+ /** The tables the squares' symbols come from. */
41
+ const TABLES = { planet: planets, element: elements, zodiac };
42
+ /** The magick-data table a symbol of the cross is a row of. */
43
+ export function fylfotSymbolTable(id) {
44
+ if (id === "sol")
45
+ return "planet";
46
+ if (["earth", "air", "water", "fire"].includes(id))
47
+ return "element";
48
+ return "zodiac";
49
+ }
50
+ /**
51
+ * The table's class, which scopes its stylesheet: a plain `<style>`, so the
52
+ * component needs no CSS tooling (it was Next's styled-jsx in the app).
53
+ */
54
+ const CLASS = "magick-fylfot-cross";
55
+ /** The custom property `ink` sets on the table, which the borders read. */
56
+ const INK = "--magick-fylfot-cross-ink";
57
+ const STYLE = `
58
+ .${CLASS} {
59
+ margin: 20px auto;
60
+ border-collapse: collapse;
61
+ }
62
+ .${CLASS} td {
63
+ width: 1.5em;
64
+ font-size: 200%;
65
+ text-align: center;
66
+ vertical-align: middle;
67
+ }
68
+ .${CLASS} td:not(:empty) {
69
+ border: 1px solid var(${INK}, black);
70
+ }
71
+ `;
72
+ /** The fylfot cross of the signs and elements, as a table of squares. */
73
+ export function FylfotCross({ cells = FYLFOT_CELLS, onCellClick, showColors = true, ink, }) {
74
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: STYLE }), _jsx("table", { className: CLASS, style: ink === undefined ? undefined : { [INK]: ink }, children: _jsx("tbody", { children: cells.map((row, rowIndex) => (_jsx("tr", { children: row.map((id, colIndex) => {
75
+ const expected = FYLFOT_CELLS[rowIndex]?.[colIndex] ?? null;
76
+ const isCorrect = id === expected;
77
+ const onClick = onCellClick && id !== null && id !== "cursor"
78
+ ? () => onCellClick({
79
+ id,
80
+ row: rowIndex,
81
+ column: colIndex,
82
+ expected,
83
+ })
84
+ : undefined;
85
+ let background = undefined;
86
+ let color = undefined;
87
+ const opacity = isCorrect ? 1 : 0.5;
88
+ if (showColors && id !== "sol") {
89
+ const element = elementFromPos(rowIndex, colIndex);
90
+ background = colorFromElement[element];
91
+ color = flashingColorFromColor[background];
92
+ }
93
+ const style = { background, color, opacity };
94
+ if (id === null)
95
+ return _jsx("td", {}, colIndex);
96
+ if (id === "")
97
+ return (_jsx("td", { onClick: onClick, style: {
98
+ ...style,
99
+ cursor: onCellClick ? "pointer" : undefined,
100
+ }, children: "\u00A0" }, colIndex));
101
+ if (id === "cursor")
102
+ return (_jsx("td", { style: {
103
+ ...style,
104
+ border: "2px solid " + (showColors ? color : "red"),
105
+ }, children: "\u00A0" }, colIndex));
106
+ const item = TABLES[fylfotSymbolTable(id)][id];
107
+ // console.log([id, type, item]);
108
+ return (_jsx("td", { onClick: onClick, style: {
109
+ ...style,
110
+ cursor: onCellClick ? "pointer" : undefined,
111
+ // Inline, so no page font overrides it; a value with
112
+ // `!important` React drops on the client.
113
+ fontFamily: "monospace",
114
+ }, children:
115
+ // U+FE0E is Unicode Variation Selector VS15,
116
+ // i.e. prefer text character to emoji, so
117
+ // we can style it ourselves.
118
+ item.symbol + "\uFE0E" }, colIndex));
119
+ }) }, rowIndex))) }) })] }));
120
+ }
121
+ export default FylfotCross;
@@ -0,0 +1,10 @@
1
+ import { type TreeOfLifeProps } from "../kabbalah/TreeOfLife.tsx";
2
+ /**
3
+ * The Tree of Life as the map of the Golden Dawn's grades: each sphere
4
+ * labelled with its grade's number and name, with the grade's element,
5
+ * order and planet below. Like the Tree, it links nothing unless given
6
+ * `sephirahHref` or `pathHref` (a sphere's row names its grade as
7
+ * `gdGradeId`). Any of `TreeOfLife`'s props override these.
8
+ */
9
+ export declare function GradeTree({ ...props }: TreeOfLifeProps): import("react").JSX.Element;
10
+ export default GradeTree;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { TreeOfLife } from "../kabbalah/TreeOfLife.js";
3
+ /*
4
+ * The Tree with each sephirah numbered and named by its grade.
5
+ *
6
+ * A Server Component, as every page that draws it is (/gd through Tiles,
7
+ * /gd/grades and each grade's page). Nothing here needs the client, and
8
+ * marked "use client" it carried the barrel the Tree reads into those
9
+ * routes' client chunks (plan 036, decision 10).
10
+ */
11
+ /**
12
+ * The Tree of Life as the map of the Golden Dawn's grades: each sphere
13
+ * labelled with its grade's number and name, with the grade's element,
14
+ * order and planet below. Like the Tree, it links nothing unless given
15
+ * `sephirahHref` or `pathHref` (a sphere's row names its grade as
16
+ * `gdGradeId`). Any of `TreeOfLife`'s props override these.
17
+ */
18
+ export function GradeTree({ ...props }) {
19
+ return (_jsx(TreeOfLife, { field: "gdGrade.id", topText: "gdGrade.name", bottomText: "gdGrade.element.symbol,gdGrade.orderId,gdGrade.planet.symbol", ...props }));
20
+ }
21
+ export default GradeTree;
@@ -0,0 +1,31 @@
1
+ import type * as React from "react";
2
+ /** The officers whose lamen `Lamen` draws; any other draws the same plain one. */
3
+ export declare const LAMEN_OFFICERS: ReadonlySet<string>;
4
+ /**
5
+ * A Golden Dawn officer's lamen: the collar's ring, the disc and the
6
+ * officer's glyph, in a −50…50 box. It defines no ids, so any number can
7
+ * share a page.
8
+ */
9
+ export declare function Lamen({ officer, grade, width, height, ringStyle, discStyle, }: {
10
+ /**
11
+ * The officer, in camel case (`hierophant`, `pastHierophant`,
12
+ * `chiefAdept`; `LAMEN_OFFICERS` lists them). Any other draws a plain
13
+ * lamen, and an empty one the Hierophant's.
14
+ */
15
+ officer: string;
16
+ /**
17
+ * The grade whose lamen to draw, for an officer who wears another in it:
18
+ * `portal` for the Portal's Hiereus and Hegemon. Otherwise the Outer
19
+ * Order's lamen.
20
+ */
21
+ grade?: string;
22
+ /** The SVG's width; from the height when not given, as it is square. */
23
+ width?: number | string;
24
+ /** The SVG's height; from the width when not given, as it is square. */
25
+ height?: number | string;
26
+ /** Style for the collar's ring, the outer circle. */
27
+ ringStyle?: React.CSSProperties;
28
+ /** Style for the disc, the inner circle. */
29
+ discStyle?: React.CSSProperties;
30
+ }): React.JSX.Element;
31
+ export default Lamen;
@@ -0,0 +1,317 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { flipped } from "./hall/art/geometry.js";
3
+ import { CupOnOctagram, PortalHegemon, PortalHiereus, RoseCross, TriangleInPentagram, } from "./lamenGlyphs.js";
4
+ /**
5
+ * Lamens bearing the Calvary cross, by officer. The Hierophant is not a
6
+ * Temple Chief but has the Imperator's cross. Looked up in a `Map`, so an
7
+ * officer name such as "constructor" is simply not found.
8
+ *
9
+ * The ring shows the officer's collar, by Gadi's own convention, so the
10
+ * roles stay distinguishable even as 20 px ritual role icons: Imperator
11
+ * red, Praemonstrator blue, Cancellarius yellow on a purple disc,
12
+ * Hierophant red and white stripes, Past Hierophant white, every lamen
13
+ * the same size. The sources differ: Regardie (*The Golden Dawn*) hangs
14
+ * the Hierophant's lamen from a white collar, the Imperator's scarlet,
15
+ * the Praemonstrator's blue and the Cancellarius' purple, and gives the
16
+ * Past Hierophant only "a Jewel of that Lamen"; the Ciceros paint the
17
+ * ring in the cross's colour and draw the Past Hierophant's lamen
18
+ * smaller; red and white stripes are the Hierophant's nemyss in Zalewski
19
+ * and in the Ciceros.
20
+ *
21
+ * Sources, by title, edition and page (plan 061, decision 9):
22
+ * - Israel Regardie, *The Golden Dawn*, vol. 3 (Aries Press, 1939), Z.1:
23
+ * pp. 82–84, the Chiefs' lamens and collars and "a Jewel of that Lamen"
24
+ * for a Past Hierophant; pp. 88–90, the Hierophant's lamen on a white
25
+ * collar.
26
+ * - Pat Zalewski, *Secret Teachings of the Golden Dawn, Book 1: The
27
+ * Neophyte Ritual 0=0* (Llewellyn, 1991): p. 76, the Hierophant's
28
+ * scarlet-and-white-striped nemyss; p. 78 (Fig. 26), the lamen drawn
29
+ * with an outer ring; p. 87, Whare Ra's white-field variant.
30
+ * - Chic and Sandra Tabatha Cicero, *Secrets of a Golden Dawn Temple*
31
+ * (Thoth, 2004): pp. 80–81, the collars and the Past Hierophant's
32
+ * smaller lamen; pp. 96–97, the ring in the charge colour.
33
+ */
34
+ const CROSS_LAMENS = new Map([
35
+ ["imperator", { ring: "red", disc: "green" }],
36
+ ["praemonstrator", { ring: "#48f", disc: "orange" }],
37
+ ["cancellarius", { ring: "yellow", disc: "#b5b" }],
38
+ ["hierophant", { ring: "stripes", disc: "green", cross: "red" }],
39
+ ["pastHierophant", { ring: "white", disc: "green", cross: "red" }],
40
+ ]);
41
+ /**
42
+ * The adepts' lamens, from the Portal's rubric (Regardie, *The Golden
43
+ * Dawn*, vol. 2, Aries Press, 1938, p. 156; archive.org
44
+ * `IsraelRegardie-TheGoldenDawn-Vol2-1938`, leaf n149): the Chief Adept's
45
+ * Rose-Cross on a yellow collar, the Second Adept's red triangle in a
46
+ * green pentagram on a white one, the Third Adept's blue cup on an orange
47
+ * octagram on a blue one. The ring shows the collar, as for the Chiefs;
48
+ * the Third Adept's blue is the Praemonstrator's. These are the Portal's
49
+ * lamens only: in a 5=6 hall, under the same role names, the Adepts wear
50
+ * the Rose-Cross, the Second's and Third's by inference
51
+ * (`lamenGrades.ts`).
52
+ */
53
+ const GLYPH_LAMENS = new Map([
54
+ ["chiefAdept", { ring: "yellow", Glyph: RoseCross }],
55
+ ["secondAdept", { ring: "white", Glyph: TriangleInPentagram }],
56
+ ["thirdAdept", { ring: "#48f", Glyph: CupOnOctagram }],
57
+ ]);
58
+ /**
59
+ * The lamens an officer wears in a particular grade instead of the Outer
60
+ * Order's, by `grade:officer` (`lamenGrades.ts`), from the Portal's
61
+ * rubric (Regardie 1938, vol. 2, p.156, leaf n149). The Hiereus wears a
62
+ * white hexagram on the four colours of Malkuth. His ring is red: the
63
+ * p.156 list gives him two collars and no cloak ("Black Collar", then
64
+ * "Red Shoes and Collar"), and we read "Black Collar" as a misprint for
65
+ * "Black Cloak", which keeps the Outer Order's scarlet collar (Z.1,
66
+ * vol. 3, pp.92–93, leaves n84–85). Should Gadi choose black instead,
67
+ * the ring becomes a dark grey (about `#333`) with the usual outline.
68
+ * The Hegemon wears a red and blue hexagram on a white ground, on a
69
+ * yellow collar.
70
+ */
71
+ const GRADE_LAMENS = new Map([
72
+ ["portal:hiereus", { ring: "red", Glyph: PortalHiereus }],
73
+ ["portal:hegemon", { ring: "yellow", disc: "white", Glyph: PortalHegemon }],
74
+ ]);
75
+ /**
76
+ * The Hegemon's Calvary cross is black on a white disc: "the black
77
+ * Calvary Cross" on a black collar (Regardie, *The Golden Dawn*, vol. 3,
78
+ * Aries Press, 1939, Z.1, p. 95; archive.org
79
+ * `IsraelRegardie-TheGoldenDawn-Vol3-1939`, leaf n87), and Mathers'
80
+ * manuscript sheet of the lamens has a black cross on white with a black
81
+ * border. The other floor officers' glyphs are white on black. His ring
82
+ * stays white, as every floor officer's, rather than his black collar's,
83
+ * which would vanish into the outline: the rings are our convention.
84
+ */
85
+ const HEGEMON_DISC = "white";
86
+ const HEGEMON_CROSS = "#000";
87
+ /** The officers whose lamen `Lamen` draws; any other draws the same plain one. */
88
+ export const LAMEN_OFFICERS = new Set([
89
+ ...CROSS_LAMENS.keys(),
90
+ ...GLYPH_LAMENS.keys(),
91
+ "hegemon",
92
+ "hiereus",
93
+ "stolistes",
94
+ "dadouchos",
95
+ "keryx",
96
+ "sentinel",
97
+ ]);
98
+ /** The ring's outer radius, inside the −50…50 viewBox; the disc's is 42. */
99
+ const RING_RADIUS = 49;
100
+ /** Height of each of the Hierophant's ring stripes, in lamen units. */
101
+ const STRIPE = 8;
102
+ /**
103
+ * The part of the ring's disc between heights `a` and `b`, as a path: the
104
+ * two arcs of the circle and the chords joining them.
105
+ */
106
+ function discSlab(a, b, r = RING_RADIUS) {
107
+ const top = Math.max(a, -r);
108
+ const bottom = Math.min(b, r);
109
+ const half = (y) => +Math.sqrt(r * r - y * y).toFixed(3);
110
+ const [xt, xb] = [half(top), half(bottom)];
111
+ return `M${xt} ${top}A${r} ${r} 0 0 1 ${xb} ${bottom}H${-xb}A${r} ${r} 0 0 1 ${-xt} ${top}Z`;
112
+ }
113
+ /**
114
+ * The white stripes of the Hierophant's ring, drawn over its red fill: a
115
+ * red stripe is centred on the horizontal diameter, so the ring is
116
+ * symmetric top to bottom (red, then white, every 8 units outward). Plain
117
+ * geometry under the disc rather than a `<pattern>`, so the lamen defines
118
+ * no ids and any number of them can share a page, inside a hall's `<defs>`
119
+ * or in renderers without hooks.
120
+ */
121
+ const RING_STRIPES = [1, 3, 5]
122
+ .flatMap((n) => [
123
+ discSlab((n - 0.5) * STRIPE, (n + 0.5) * STRIPE),
124
+ discSlab(-(n + 0.5) * STRIPE, -(n - 0.5) * STRIPE),
125
+ ])
126
+ .join("");
127
+ const TipharethCalvaryCross = ({ style, scale = 7.5, }) => (_jsx("path", { d: "M -1.1 -4.8 v.708a3.045 3.045 0 0 0-1.842 1.806h-.64v2.02h.601a3.045 3.045 0 0 0 1.881 1.91v3.134h2.02v-3.14a3.045 3.045 0 0 0 1.867-1.904h.615v-2.02h-.653a3.045 3.045 0 0 0-1.829-1.8v-.714zm2.02 1.256a2.534 2.534 0 0 1 1.285 1.258h-1.285zm-2.02 0v1.258h-1.292a2.534 2.534 0 0 1 1.292-1.257zm-1.334 3.278h1.334v1.361a2.534 2.534 0 0 1-1.334-1.36zm3.354 0h1.329a2.534 2.534 0 0 1-1.329 1.362z", style: {
128
+ fill: "red",
129
+ stroke: "black",
130
+ strokeWidth: 1 / scale - 0.05,
131
+ ...style,
132
+ }, transform: "scale(" + scale + ")" }));
133
+ /**
134
+ * The caduceus of the Keryx's lamen, drawn from geometry in lamen units
135
+ * (plan 061, stage H, part 2, follow-ups: Gadi chose wings V1 with two
136
+ * turns of the serpents on 26 October 2026). A pointed staff topped by a
137
+ * ringed ball, two wings, and two serpents wound round the staff as a
138
+ * double helix seen a little from above, their heads turned outward
139
+ * below the wings.
140
+ *
141
+ * Each serpent winds `x = A(u)·sin θ` from the tail (u = 0) to the neck
142
+ * (u = 1), with a small tilt `TILT · dy/dθ · cos θ` added to y that
143
+ * rounds the loops. The reach `A` falls towards the tail, the turning
144
+ * rate rises there (the coils tighten), and the body thins arc by arc.
145
+ * The serpent is cut into half-turn arcs where it turns at a side: the
146
+ * arcs behind (cos θ < 0) are drawn under the staff, those in front over
147
+ * it on a band of the disc's black, so the depth settles every crossing.
148
+ * Each arc is a few Hermite cubics through eighth-turn knots, rounded to
149
+ * one decimal: no ids, no gradients.
150
+ */
151
+ const TURNS = 2;
152
+ /** The body's width at the neck, and the share of it left at the tail. */
153
+ const BODY = 5;
154
+ const THIN = 0.6;
155
+ /** The black edge either side of a front arc and of the staff. */
156
+ const EDGE = 1.25;
157
+ /** The widest swing, at the neck, and the share of it left at the tail. */
158
+ const REACH = 12;
159
+ const TAPER = 0.4;
160
+ /** How much faster the coils turn at the tail than at the neck (+90%). */
161
+ const TIGHTEN = 0.9;
162
+ const TAIL = 35;
163
+ const NECK = -10;
164
+ const TILT = 0.3;
165
+ /** Knots per serpent: one every eighth of a turn. */
166
+ const KNOTS = TURNS * 8;
167
+ const r1 = (value) => Math.round(value * 10) / 10 + 0;
168
+ const xy = ([x, y]) => `${r1(x)},${r1(y)}`;
169
+ /**
170
+ * How far along the serpent (u, tail 0 to neck 1) it has made `share` of
171
+ * its turns, the turning rate falling linearly from 1 + TIGHTEN at the
172
+ * tail to 1 at the neck.
173
+ */
174
+ const along = (share) => (1 +
175
+ TIGHTEN -
176
+ Math.sqrt((1 + TIGHTEN) ** 2 - 2 * TIGHTEN * share * (1 + TIGHTEN / 2))) /
177
+ TIGHTEN;
178
+ /** The angle at `share` of the turns: π/2 at the neck (and the tail). */
179
+ const angle = (side, share) => Math.PI / 2 + 2 * Math.PI * TURNS * (share - 1) + (side < 0 ? Math.PI : 0);
180
+ /** A serpent's centre line at `share` of its turns. */
181
+ function coil(side, share) {
182
+ const u = along(share);
183
+ const theta = angle(side, share);
184
+ // dy/dθ: how far the coil falls per radian here.
185
+ const pitch = ((NECK - TAIL) * (1 + TIGHTEN / 2)) /
186
+ ((1 + TIGHTEN - TIGHTEN * u) * 2 * Math.PI * TURNS);
187
+ return [
188
+ REACH * (TAPER + (1 - TAPER) * u) * Math.sin(theta),
189
+ TAIL + (NECK - TAIL) * u + TILT * pitch * Math.cos(theta),
190
+ ];
191
+ }
192
+ /** The centre line from share `a` to `b` in `pieces` Hermite cubics. */
193
+ function coilPath(side, a, b, pieces) {
194
+ const tangent = (share) => {
195
+ const h = 1e-4;
196
+ const [p, q] = [coil(side, share - h), coil(side, share + h)];
197
+ return [(q[0] - p[0]) / (2 * h), (q[1] - p[1]) / (2 * h)];
198
+ };
199
+ let d = `M${xy(coil(side, a))}`;
200
+ for (let i = 0; i < pieces; i++) {
201
+ const [s0, s1] = [
202
+ a + ((b - a) * i) / pieces,
203
+ a + ((b - a) * (i + 1)) / pieces,
204
+ ];
205
+ const k = (s1 - s0) / 3;
206
+ const [p0, p1, t0, t1] = [
207
+ coil(side, s0),
208
+ coil(side, s1),
209
+ tangent(s0),
210
+ tangent(s1),
211
+ ];
212
+ d += `C${xy([p0[0] + t0[0] * k, p0[1] + t0[1] * k])} ${xy([p1[0] - t1[0] * k, p1[1] - t1[1] * k])} ${xy(p1)}`;
213
+ }
214
+ return d;
215
+ }
216
+ const BEHIND = [];
217
+ const IN_FRONT = [];
218
+ /** Each head: its centre, and the angle it leans out. */
219
+ const HEADS = [];
220
+ for (const side of [1, -1]) {
221
+ // Half-turn arcs, from one side turn to the next: the tail is at a side
222
+ // too, as TURNS is whole.
223
+ for (let i = 0; i < KNOTS; i += 4) {
224
+ const [a, b] = [i / KNOTS, (i + 4) / KNOTS];
225
+ const width = r1(BODY * (THIN + (1 - THIN) * along((a + b) / 2)));
226
+ const d = coilPath(side, a, b, 4);
227
+ if (Math.cos(angle(side, (a + b) / 2)) < 0)
228
+ BEHIND.push({ d, width });
229
+ else {
230
+ // The black band only over the arc's middle, where it crosses the
231
+ // staff and the other serpent, clear of the turns at the sides.
232
+ const trim = (b - a) * 0.17;
233
+ IN_FRONT.push({
234
+ d: coilPath(side, a + trim, b - trim, 3),
235
+ width: width + 2 * EDGE,
236
+ band: true,
237
+ }, { d, width });
238
+ }
239
+ }
240
+ // The neck rises and leans outward to the head.
241
+ const [x, y] = coil(side, 1);
242
+ const [hx, hy] = [x + side * 2.6, y - 3];
243
+ IN_FRONT.push({
244
+ d: `M${xy([x, y])}Q${xy([x + side * 0.3, y - 2.2])} ${xy([hx, hy])}`,
245
+ width: BODY,
246
+ });
247
+ HEADS.push({ cx: r1(hx + side * 1.2), cy: r1(hy - 1), lean: -side * 35 });
248
+ }
249
+ /** The staff, pointed at the foot, its top hidden by the ball. */
250
+ const STAFF = "M-1.7,-26H1.7V36L0,38.4L-1.7,36Z";
251
+ /**
252
+ * One wing (the right), in lamen units; flipped for the left. Design V1
253
+ * ("Arch"): the top edge leaves the ball's side, dips under it (the notch
254
+ * the ball's ring closes), rises to a crest beside the ball and falls to
255
+ * a far, low tip; the underside is flat, cut into four round feathers.
256
+ */
257
+ const WING = "M0,-24.5Q3.8,-20.4 6,-21C9.5,-22.5 9,-30.6 14,-31Q25.8,-25.2 35.5,-16.5C35.5,-14.4 30.9,-13.8 29.3,-16.6C29.3,-13.6 24.1,-13 22.5,-16.2C22.5,-13.2 16.7,-12.6 15.1,-15.8C15.1,-12.8 8.6,-12.2 7,-15.4Q2.4,-14.9 0,-19Z";
258
+ const WINGS = WING + flipped(WING);
259
+ /** The Keryx's caduceus, white on the black disc, in lamen units. */
260
+ function Caduceus() {
261
+ const strokes = (list) => list.map(({ d, width, band }) => band ? (_jsx("path", { d: d, stroke: "black", strokeWidth: width, strokeLinecap: "butt" }, `b${d}`)) : (_jsx("path", { d: d, strokeWidth: width }, d)));
262
+ return (_jsxs("g", { fill: "white", children: [_jsxs("g", { fill: "none", stroke: "white", strokeLinecap: "round", children: [strokes(BEHIND), _jsx("path", { d: STAFF, fill: "white", stroke: "black", strokeWidth: 2 * EDGE, paintOrder: "stroke" }), strokes(IN_FRONT)] }), _jsx("path", { d: WINGS }), _jsx("g", { stroke: "black", strokeWidth: 1.2, children: HEADS.map(({ cx, cy, lean }) => (_jsx("ellipse", { cx: cx, cy: cy, rx: r1(BODY * 0.9), ry: r1(BODY * 0.6), transform: `rotate(${lean} ${cx} ${cy})` }, lean))) }), _jsx("circle", { cy: -27, r: 4.8, stroke: "black", strokeWidth: 1.6 })] }));
263
+ }
264
+ /**
265
+ * The Sentinel's Eye of Horus (the wedjat), drawn from a few strokes after
266
+ * its ancient form (plan 061, stage H): the brow, the eye with its pupil,
267
+ * the line from the outer corner, the drop and the spiral. White on the
268
+ * black disc.
269
+ */
270
+ function EyeOfHorus() {
271
+ return (_jsxs("g", { transform: "translate(-2 2)", fill: "white", stroke: "white", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M-30,-19C-16,-28 12,-29 33,-22L33,-16C12,-23 -16,-22 -30,-14Z", stroke: "none" }), _jsx("path", { d: "M-30,-3C-18,-15 10,-16 26,-5C12,3 -16,5 -30,-3Z", fill: "none", strokeWidth: 4.2 }), _jsx("path", { d: "M26,-5H38", strokeWidth: 4.6 }), _jsx("circle", { cx: -3, cy: -6.5, r: 7.2, stroke: "none" }), _jsx("path", { d: "M-11,1.5L-14,1.5L-13,28L-11.6,30Z", stroke: "none" }), _jsx("path", { d: "M2,2C5,15 13,22 22,22C30,22 32,13 26,11C21.5,9.5 19,14 22.5,16", fill: "none", strokeWidth: 4 })] }));
272
+ }
273
+ // Every lamen is drawn for magick.ly: the Keryx's caduceus and the
274
+ // Sentinel's Eye of Horus from geometry since stage H of plan 061 (they
275
+ // were an SVG Repo icon and a game-icons.net path before), the adepts'
276
+ // since its second part.
277
+ /**
278
+ * A Golden Dawn officer's lamen: the collar's ring, the disc and the
279
+ * officer's glyph, in a −50…50 box. It defines no ids, so any number can
280
+ * share a page.
281
+ */
282
+ export function Lamen({ officer, grade, width, height, ringStyle, discStyle, }) {
283
+ if (!officer)
284
+ officer = "hierophant";
285
+ const gradeLamen = grade === undefined ? undefined : GRADE_LAMENS.get(`${grade}:${officer}`);
286
+ // A grade's own lamen replaces the officer's white-on-black glyph.
287
+ const glyph = gradeLamen ? "" : officer;
288
+ const crossLamen = CROSS_LAMENS.get(officer);
289
+ const glyphLamen = gradeLamen ?? GLYPH_LAMENS.get(officer);
290
+ const _ringStyle = {
291
+ color: "#000",
292
+ overflow: "visible",
293
+ fill: crossLamen?.ring === "stripes"
294
+ ? "red"
295
+ : (crossLamen?.ring ?? glyphLamen?.ring ?? "white"),
296
+ stroke: "#000",
297
+ strokeWidth: 1,
298
+ strokeDasharray: "none",
299
+ paintOrder: "stroke fill markers",
300
+ stopColor: "#000",
301
+ ...ringStyle,
302
+ };
303
+ const _discStyle = {
304
+ color: "#000",
305
+ overflow: "visible",
306
+ fill: crossLamen?.disc ??
307
+ glyphLamen?.disc ??
308
+ (glyph === "hegemon" ? HEGEMON_DISC : "#000"),
309
+ stroke: "#000",
310
+ strokeWidth: 1,
311
+ paintOrder: "stroke fill markers",
312
+ stopColor: "#000",
313
+ ...discStyle,
314
+ };
315
+ return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", xmlSpace: "preserve", width: width, height: height, viewBox: "-50 -50 100 100", children: [_jsx("circle", { cx: 0, cy: 0, r: RING_RADIUS, style: _ringStyle }), crossLamen?.ring === "stripes" && _jsx("path", { d: RING_STRIPES, fill: "white" }), _jsx("circle", { cx: 0, cy: 0, r: 42, style: _discStyle }), crossLamen && (_jsx(TipharethCalvaryCross, { style: { fill: crossLamen.cross ?? crossLamen.ring } })), glyph === "hegemon" && (_jsx("path", { d: "M 0 0 h2.482v2.02h-2.482v5.043h-2.02v-5.043h-2.482v-2.02h2.481v-2.515h2.02z", style: { fill: HEGEMON_CROSS }, transform: "scale(7.2) translate(1,-2.2)" })), glyph === "hiereus" && (_jsx("path", { d: "m114.746 96.37-4.93 8.54h9.86zm0 2.13 3.087 5.346h-6.174z", style: { fill: "white" }, transform: "scale(6) translate(-114.7,-101.5)" })), glyph === "stolistes" && (_jsx("path", { d: "M79.292 93.798H69.273l5.002-6.66zm-2.397-8.903a2.62 2.62 0 0 1-2.62 2.62 2.62 2.62 0 0 1-2.622-2.62 2.62 2.62 0 0 1 2.621-2.62 2.62 2.62 0 0 1 2.621 2.62zm-9.152-10.753H80.81c-.448 4.994-3.182 7.714-6.523 8.346-3.279-.596-6.12-3.083-6.544-8.346z", style: { fill: "white" }, transform: "scale(3.3) translate(-74.2,-83.5)" })), glyph === "dadouchos" && (_jsx("path", { d: "M30.12 92.248V88.65h3.598v3.598zm0-3.587v-3.597h3.598v3.597zm0-3.587v-3.597h3.598v3.597zm3.587 0v-3.597h3.597v3.597zm3.587 0v-3.597h3.597v3.597zm10.767 7.174h-3.597V88.65h3.597zm-3.587 0h-3.597V88.65h3.597zm-3.587 0H37.29V88.65h3.597zm0-3.587H37.29v-3.597h3.597zm0-3.587H37.29v-3.597h3.597zM48.061 74.3v3.597h-3.597v-3.597zm0 3.587v3.597h-3.597v-3.597zm0 3.587v3.597h-3.597v-3.597zm-3.587 0v3.597h-3.597v-3.597zm-3.587 0v3.597H37.29v-3.597Zm-10.766-7.182h3.597v3.597H30.12Zm3.586 0h3.598v3.597h-3.598zm3.587 0h3.597v3.597h-3.597zm0 3.587h3.597v3.597h-3.597zm0 3.587h3.597v3.597h-3.597z", style: { fill: "white" }, transform: "scale(2.9) translate(-39,-83.5)" })), glyph === "keryx" && _jsx(Caduceus, {}), glyph === "sentinel" && _jsx(EyeOfHorus, {}), glyphLamen && _jsx(glyphLamen.Glyph, {})] }));
316
+ }
317
+ export default Lamen;
@@ -0,0 +1,31 @@
1
+ import React from "react";
2
+ import { type RoseVersionId } from "./roseSigilGeometry.ts";
3
+ export interface RoseSigilProps {
4
+ /**
5
+ * The text to draw. Final forms are drawn as their base letters, and
6
+ * anything not one of the 22 letters (spaces, points, Latin) is left out.
7
+ */
8
+ text: string;
9
+ /** Draws the rose's three rings of letters behind the sigil. */
10
+ showRose?: boolean;
11
+ /**
12
+ * Which rose the sigil is traced on (`ROSE_VERSIONS`): the Golden
13
+ * Dawn's (`gd`) when not given or not a version, or Regardie's of 1939
14
+ * (`regardie1939`), which exchanges Kaph and Tau. `ROSE_VERSION_NOTES`
15
+ * says where each comes from.
16
+ */
17
+ version?: RoseVersionId;
18
+ /** Draws the connecting path on, once the layout is optimised. */
19
+ animate?: boolean;
20
+ /** The `<svg>`, for exporting the drawing. */
21
+ ref?: React.Ref<SVGSVGElement>;
22
+ }
23
+ /**
24
+ * The rose-cross sigil of a Hebrew text, drawn through the letters' places
25
+ * on the rose. It renders the letter centres first, on the server too, then
26
+ * spreads repeated and aligned letters with the optimiser once in the
27
+ * browser, and draws the path on. A client component: the server-safe
28
+ * drawing is `RoseSigilImage`.
29
+ */
30
+ export declare function RoseSigil({ text, showRose, version, animate, ref, }: RoseSigilProps): React.JSX.Element;
31
+ export default RoseSigil;
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { RoseSigilImage } from "./RoseSigilImage.js";
5
+ import { sigilPoints, } from "./roseSigilGeometry.js";
6
+ import { optimizeSigilPoints } from "./roseSigilOptimizer.js";
7
+ /**
8
+ * The rose-cross sigil of a Hebrew text, drawn through the letters' places
9
+ * on the rose. It renders the letter centres first, on the server too, then
10
+ * spreads repeated and aligned letters with the optimiser once in the
11
+ * browser, and draws the path on. A client component: the server-safe
12
+ * drawing is `RoseSigilImage`.
13
+ */
14
+ export function RoseSigil({ text, showRose = true, version, animate = true, ref, }) {
15
+ const pathRef = React.useRef(null);
16
+ // useId yields ":r1:"-style ids; CSS identifiers need the colons removed.
17
+ const animation = `rose-sigil-draw-${React.useId().replace(/[^\w-]/g, "")}`;
18
+ const points = React.useMemo(() => sigilPoints(text, version), [text, version]);
19
+ const [optimized, setOptimized] = React.useState(null);
20
+ React.useEffect(() => {
21
+ if (!points.length)
22
+ return;
23
+ let cancelled = false;
24
+ optimizeSigilPoints(points).then((result) => {
25
+ if (!cancelled)
26
+ setOptimized(result);
27
+ },
28
+ // Without the optimiser (its WASM failed to load) the sigil keeps
29
+ // the letter centres it was first drawn with.
30
+ (error) => console.error(error));
31
+ return () => {
32
+ cancelled = true;
33
+ };
34
+ }, [points]);
35
+ React.useEffect(() => {
36
+ if (pathRef.current && optimized) {
37
+ const length = pathRef.current.getTotalLength();
38
+ pathRef.current.style.strokeDasharray = animate ? length.toString() : "";
39
+ pathRef.current.style.strokeDashoffset = animate ? length.toString() : "";
40
+ pathRef.current.classList.remove(animation);
41
+ // @ts-expect-error: trick to trigger reflow
42
+ void pathRef.current.offsetWidth;
43
+ if (animate)
44
+ pathRef.current.classList.add(animation);
45
+ }
46
+ }, [animate, animation, optimized]);
47
+ return (_jsx(RoseSigilImage, { text: text, showRose: showRose, version: version, debug: false, points: optimized || points, animation: animate ? animation : undefined, ref: ref, pathRef: pathRef }));
48
+ }
49
+ export default RoseSigil;
@@ -0,0 +1,38 @@
1
+ import React from "react";
2
+ import { type RoseVersionId, type SigilPoint } from "./roseSigilInternals.ts";
3
+ export interface RoseSigilImageProps {
4
+ /**
5
+ * The text to draw. Final forms are drawn as their base letters, and
6
+ * anything not one of the 22 letters (spaces, points, Latin) is left out.
7
+ */
8
+ text: string;
9
+ /** Draws the rose's three rings of letters behind the sigil (default true). */
10
+ showRose?: boolean;
11
+ /**
12
+ * Which rose (`ROSE_VERSIONS`): the Golden Dawn's (`gd`) when not given
13
+ * or not a version, or Regardie's of 1939 (`regardie1939`), which
14
+ * exchanges Kaph and Tau. Give the `points` of the same version.
15
+ */
16
+ version?: RoseVersionId;
17
+ /** Draws each letter's circle and the unoptimised path too (default false). */
18
+ debug?: boolean;
19
+ /** Letter centres to draw through; the optimised layout when available. */
20
+ points: SigilPoint[];
21
+ /**
22
+ * Draw-on animation class for the connecting path, unique per instance
23
+ * because a `<style>` inside inline SVG is document-wide; server renders
24
+ * never animate.
25
+ */
26
+ animation?: string;
27
+ /** The `<svg>`, for exporting the drawing. */
28
+ ref?: React.Ref<SVGSVGElement>;
29
+ /** The connecting path, which `RoseSigil` animates. */
30
+ pathRef?: React.Ref<SVGPathElement>;
31
+ }
32
+ /**
33
+ * The sigil's artwork with no hooks, so that a server-side renderer can
34
+ * draw it with React's server build; `RoseSigil` adds the optimiser and
35
+ * the animation around it.
36
+ */
37
+ export declare function RoseSigilImage({ text, showRose, version, debug, points, animation, ref, pathRef, }: RoseSigilImageProps): React.JSX.Element;
38
+ export default RoseSigilImage;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { svgCoordinate } from "../svgCoordinate.js";
4
+ import { letterPoint, pathFromPoints, roseLetters, roseRings, sigilPoints, } from "./roseSigilInternals.js";
5
+ const roseStrokeColor = "rgba(0,0,0,.4)";
6
+ /**
7
+ * The sigil's artwork with no hooks, so that a server-side renderer can
8
+ * draw it with React's server build; `RoseSigil` adds the optimiser and
9
+ * the animation around it.
10
+ */
11
+ export function RoseSigilImage({ text, showRose = true, version, debug = false, points, animation, ref, pathRef, }) {
12
+ const sigilTokens = roseLetters(text);
13
+ return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", version: "1.1", viewBox: "-50 -50 100 100", ref: ref, children: [animation && (_jsx("style", { children: `.${animation} { animation: ${animation} ${sigilTokens.length / 4}s linear 0s forwards 1; }
14
+ @keyframes ${animation} { to { stroke-dashoffset: 0; } }` })), showRose && (_jsxs(_Fragment, { children: [" ", _jsx("circle", { cx: 0, cy: 0, r: 10, stroke: roseStrokeColor, strokeWidth: 0.5, fill: "none" }), roseRings(version).map((row, i) => (_jsxs(React.Fragment, { children: [_jsx("circle", { cx: 0, cy: 0, r: 10 * (i + 2), stroke: roseStrokeColor, strokeWidth: 0.5, fill: "none" }), row.map((letter) => {
15
+ // Labels sit on the letter centres the path is drawn through.
16
+ const center = letterPoint(letter, version);
17
+ const x = svgCoordinate(center.x);
18
+ const y = svgCoordinate(center.y);
19
+ return (_jsxs(React.Fragment, { children: [debug && (_jsx("circle", { cx: x, cy: y, r: 5, stroke: roseStrokeColor, strokeWidth: 0.5, strokeDasharray: 0.2, fill: "none" })), _jsx("text", { x: x, y: y, fill: roseStrokeColor, textAnchor: "middle", fontSize: 10, dominantBaseline: "middle", children: letter })] }, letter));
20
+ })] }, row.join(""))))] })), debug && (_jsx("path", {
21
+ // Letter centres before optimisation
22
+ stroke: "#aa0", fill: "none", d: pathFromPoints({
23
+ points: sigilPoints(text, version),
24
+ sigilTokens,
25
+ }) })), _jsx("path", {
26
+ // Connecting path
27
+ stroke: "red", fill: "none", ref: pathRef, d: pathFromPoints({ points, sigilTokens }) })] }));
28
+ }
29
+ export default RoseSigilImage;