schematex 0.3.0 → 0.3.2
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.
- package/dist/ai/ai-sdk.cjs +25 -25
- package/dist/ai/ai-sdk.d.cts +1 -1
- package/dist/ai/ai-sdk.d.ts +1 -1
- package/dist/ai/ai-sdk.js +20 -20
- package/dist/ai/index.cjs +28 -28
- package/dist/ai/index.d.cts +1 -1
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +20 -20
- package/dist/browser.cjs +22 -22
- package/dist/browser.js +20 -20
- package/dist/{chunk-TYCHEOQX.js → chunk-2J2QWNGI.js} +26 -20
- package/dist/chunk-2J2QWNGI.js.map +1 -0
- package/dist/{chunk-MCFQAUQV.cjs → chunk-2UJAVPA4.cjs} +58 -39
- package/dist/chunk-2UJAVPA4.cjs.map +1 -0
- package/dist/{chunk-5AEN2PLB.cjs → chunk-3YXXZ4LT.cjs} +33 -33
- package/dist/{chunk-5AEN2PLB.cjs.map → chunk-3YXXZ4LT.cjs.map} +1 -1
- package/dist/{chunk-RP5UATRA.js → chunk-56LXBM45.js} +4 -4
- package/dist/{chunk-RP5UATRA.js.map → chunk-56LXBM45.js.map} +1 -1
- package/dist/{chunk-OC22GGQN.js → chunk-6BKUD5EJ.js} +54 -4
- package/dist/chunk-6BKUD5EJ.js.map +1 -0
- package/dist/{chunk-ULYRO2KY.cjs → chunk-75BMFCP5.cjs} +44 -44
- package/dist/{chunk-ULYRO2KY.cjs.map → chunk-75BMFCP5.cjs.map} +1 -1
- package/dist/{chunk-ZNDIGQJD.js → chunk-7BEJHG43.js} +3 -3
- package/dist/{chunk-ZNDIGQJD.js.map → chunk-7BEJHG43.js.map} +1 -1
- package/dist/{chunk-WYFXOXVK.cjs → chunk-7MVDN5UC.cjs} +35 -35
- package/dist/{chunk-WYFXOXVK.cjs.map → chunk-7MVDN5UC.cjs.map} +1 -1
- package/dist/{chunk-JDTB7IKL.js → chunk-BJ65PKDU.js} +3 -3
- package/dist/{chunk-JDTB7IKL.js.map → chunk-BJ65PKDU.js.map} +1 -1
- package/dist/{chunk-FE6GAUNW.js → chunk-BJWMPPEA.js} +5 -5
- package/dist/{chunk-FE6GAUNW.js.map → chunk-BJWMPPEA.js.map} +1 -1
- package/dist/{chunk-MJGDP3CS.cjs → chunk-BUN3CRMP.cjs} +488 -109
- package/dist/chunk-BUN3CRMP.cjs.map +1 -0
- package/dist/{chunk-COLTVQWR.cjs → chunk-C3IVD7DI.cjs} +25 -25
- package/dist/{chunk-COLTVQWR.cjs.map → chunk-C3IVD7DI.cjs.map} +1 -1
- package/dist/{chunk-3YZ6FPQW.cjs → chunk-EYHD7LV3.cjs} +131 -131
- package/dist/{chunk-3YZ6FPQW.cjs.map → chunk-EYHD7LV3.cjs.map} +1 -1
- package/dist/{chunk-UGCUNADI.js → chunk-F6OROIHS.js} +35 -16
- package/dist/chunk-F6OROIHS.js.map +1 -0
- package/dist/{chunk-3FKS4KQK.cjs → chunk-GYTWJ6VB.cjs} +65 -59
- package/dist/chunk-GYTWJ6VB.cjs.map +1 -0
- package/dist/{chunk-VLMK7MQK.js → chunk-H4CJTKEH.js} +23 -4
- package/dist/chunk-H4CJTKEH.js.map +1 -0
- package/dist/{chunk-45KP67RR.js → chunk-HIQPEAL7.js} +5 -5
- package/dist/chunk-HIQPEAL7.js.map +1 -0
- package/dist/{chunk-NB56L5QK.js → chunk-IT2TVXC7.js} +4 -4
- package/dist/{chunk-NB56L5QK.js.map → chunk-IT2TVXC7.js.map} +1 -1
- package/dist/{chunk-2JDVJRR3.cjs → chunk-K5QG53GT.cjs} +28 -28
- package/dist/chunk-K5QG53GT.cjs.map +1 -0
- package/dist/{chunk-B37IKTI7.cjs → chunk-KUXOHLGC.cjs} +97 -47
- package/dist/chunk-KUXOHLGC.cjs.map +1 -0
- package/dist/{chunk-A5D2IMOX.cjs → chunk-L2KUGWFR.cjs} +47 -47
- package/dist/{chunk-A5D2IMOX.cjs.map → chunk-L2KUGWFR.cjs.map} +1 -1
- package/dist/{chunk-HDKDQAEQ.cjs → chunk-LFZZ4NCP.cjs} +14 -2
- package/dist/chunk-LFZZ4NCP.cjs.map +1 -0
- package/dist/{chunk-U6L3FAML.js → chunk-M3R6RCXY.js} +3 -3
- package/dist/{chunk-U6L3FAML.js.map → chunk-M3R6RCXY.js.map} +1 -1
- package/dist/{chunk-5YYAYW67.js → chunk-M5ZC3LFJ.js} +3 -3
- package/dist/{chunk-5YYAYW67.js.map → chunk-M5ZC3LFJ.js.map} +1 -1
- package/dist/{chunk-B6INLQBU.cjs → chunk-MKKFIPKU.cjs} +46 -46
- package/dist/{chunk-B6INLQBU.cjs.map → chunk-MKKFIPKU.cjs.map} +1 -1
- package/dist/{chunk-M5B2UUNW.js → chunk-NFT6VW73.js} +4 -4
- package/dist/{chunk-M5B2UUNW.js.map → chunk-NFT6VW73.js.map} +1 -1
- package/dist/{chunk-X7RPFTTR.cjs → chunk-OK5RYX55.cjs} +26 -26
- package/dist/{chunk-X7RPFTTR.cjs.map → chunk-OK5RYX55.cjs.map} +1 -1
- package/dist/{chunk-NNU4RGT3.js → chunk-RNGYXGHS.js} +39 -27
- package/dist/chunk-RNGYXGHS.js.map +1 -0
- package/dist/{chunk-SQKLKBBK.cjs → chunk-RODV6PC4.cjs} +25 -6
- package/dist/chunk-RODV6PC4.cjs.map +1 -0
- package/dist/{chunk-H2OEUBPO.js → chunk-SE23X5OE.js} +441 -62
- package/dist/chunk-SE23X5OE.js.map +1 -0
- package/dist/{chunk-FCGHV6ZK.js → chunk-SFSZUOFT.js} +4 -4
- package/dist/{chunk-FCGHV6ZK.js.map → chunk-SFSZUOFT.js.map} +1 -1
- package/dist/{chunk-NZH4GWE6.cjs → chunk-UK7JF5QB.cjs} +37 -37
- package/dist/{chunk-NZH4GWE6.cjs.map → chunk-UK7JF5QB.cjs.map} +1 -1
- package/dist/{chunk-QSQX77S2.cjs → chunk-W7GIQTJV.cjs} +21 -21
- package/dist/{chunk-QSQX77S2.cjs.map → chunk-W7GIQTJV.cjs.map} +1 -1
- package/dist/{chunk-KLJEK547.js → chunk-WHJXRLFD.js} +14 -3
- package/dist/chunk-WHJXRLFD.js.map +1 -0
- package/dist/{chunk-YQANC7HQ.js → chunk-X4F6VVEJ.js} +3 -3
- package/dist/{chunk-YQANC7HQ.js.map → chunk-X4F6VVEJ.js.map} +1 -1
- package/dist/{chunk-ZNOD4VZT.cjs → chunk-XI6JOG76.cjs} +46 -46
- package/dist/{chunk-ZNOD4VZT.cjs.map → chunk-XI6JOG76.cjs.map} +1 -1
- package/dist/{chunk-K2SOC3XF.cjs → chunk-XUEROLSB.cjs} +70 -19
- package/dist/chunk-XUEROLSB.cjs.map +1 -0
- package/dist/{chunk-XTATRNUN.cjs → chunk-YTEEZV6J.cjs} +442 -430
- package/dist/chunk-YTEEZV6J.cjs.map +1 -0
- package/dist/{chunk-XZNPAD6E.js → chunk-ZNLEUL7T.js} +58 -7
- package/dist/chunk-ZNLEUL7T.js.map +1 -0
- package/dist/{chunk-DNZFOCV7.js → chunk-ZTSO3S4P.js} +3 -3
- package/dist/{chunk-DNZFOCV7.js.map → chunk-ZTSO3S4P.js.map} +1 -1
- package/dist/diagrams/blockdiagram/index.cjs +6 -6
- package/dist/diagrams/blockdiagram/index.d.cts +1 -1
- package/dist/diagrams/blockdiagram/index.d.ts +1 -1
- package/dist/diagrams/blockdiagram/index.js +2 -2
- package/dist/diagrams/circuit/index.cjs +8 -8
- package/dist/diagrams/circuit/index.d.cts +1 -1
- package/dist/diagrams/circuit/index.d.ts +1 -1
- package/dist/diagrams/circuit/index.js +2 -2
- package/dist/diagrams/ecomap/index.cjs +8 -8
- package/dist/diagrams/ecomap/index.d.cts +1 -1
- package/dist/diagrams/ecomap/index.d.ts +1 -1
- package/dist/diagrams/ecomap/index.js +3 -3
- package/dist/diagrams/entity/index.cjs +6 -6
- package/dist/diagrams/entity/index.d.cts +1 -1
- package/dist/diagrams/entity/index.d.ts +1 -1
- package/dist/diagrams/entity/index.js +2 -2
- package/dist/diagrams/fishbone/index.cjs +8 -8
- package/dist/diagrams/fishbone/index.d.cts +1 -1
- package/dist/diagrams/fishbone/index.d.ts +1 -1
- package/dist/diagrams/fishbone/index.js +2 -2
- package/dist/diagrams/flowchart/index.cjs +8 -8
- package/dist/diagrams/flowchart/index.d.cts +2 -2
- package/dist/diagrams/flowchart/index.d.ts +2 -2
- package/dist/diagrams/flowchart/index.js +2 -2
- package/dist/diagrams/genogram/index.cjs +10 -10
- package/dist/diagrams/genogram/index.d.cts +1 -1
- package/dist/diagrams/genogram/index.d.ts +1 -1
- package/dist/diagrams/genogram/index.js +3 -3
- package/dist/diagrams/ladder/index.cjs +6 -6
- package/dist/diagrams/ladder/index.d.cts +1 -1
- package/dist/diagrams/ladder/index.d.ts +1 -1
- package/dist/diagrams/ladder/index.js +2 -2
- package/dist/diagrams/logic/index.cjs +6 -6
- package/dist/diagrams/logic/index.d.cts +1 -1
- package/dist/diagrams/logic/index.d.ts +1 -1
- package/dist/diagrams/logic/index.js +2 -2
- package/dist/diagrams/orgchart/index.cjs +7 -7
- package/dist/diagrams/orgchart/index.d.cts +1 -1
- package/dist/diagrams/orgchart/index.d.ts +1 -1
- package/dist/diagrams/orgchart/index.js +2 -2
- package/dist/diagrams/pedigree/index.cjs +8 -8
- package/dist/diagrams/pedigree/index.d.cts +1 -1
- package/dist/diagrams/pedigree/index.d.ts +1 -1
- package/dist/diagrams/pedigree/index.js +3 -3
- package/dist/diagrams/phylo/index.cjs +7 -7
- package/dist/diagrams/phylo/index.d.cts +1 -1
- package/dist/diagrams/phylo/index.d.ts +1 -1
- package/dist/diagrams/phylo/index.js +2 -2
- package/dist/diagrams/sld/index.cjs +6 -6
- package/dist/diagrams/sld/index.d.cts +1 -1
- package/dist/diagrams/sld/index.d.ts +1 -1
- package/dist/diagrams/sld/index.js +2 -2
- package/dist/diagrams/sociogram/index.cjs +7 -7
- package/dist/diagrams/sociogram/index.d.cts +1 -1
- package/dist/diagrams/sociogram/index.d.ts +1 -1
- package/dist/diagrams/sociogram/index.js +3 -3
- package/dist/diagrams/timing/index.cjs +5 -5
- package/dist/diagrams/timing/index.d.cts +1 -1
- package/dist/diagrams/timing/index.d.ts +1 -1
- package/dist/diagrams/timing/index.js +2 -2
- package/dist/diagrams/venn/index.cjs +9 -9
- package/dist/diagrams/venn/index.d.cts +1 -1
- package/dist/diagrams/venn/index.d.ts +1 -1
- package/dist/diagrams/venn/index.js +2 -2
- package/dist/{index-ivhNGsyU.d.cts → index-BrLxEzSQ.d.cts} +1 -1
- package/dist/{index-CUwp4GXI.d.ts → index-dWDwG6BW.d.ts} +1 -1
- package/dist/index.cjs +41 -41
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +19 -19
- package/dist/react.cjs +20 -20
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +19 -19
- package/dist/react.js.map +1 -1
- package/dist/{types-Bl-Pn7Wj.d.cts → types-BtiUg7Gx.d.cts} +24 -3
- package/dist/{types-Bl-Pn7Wj.d.ts → types-BtiUg7Gx.d.ts} +24 -3
- package/package.json +1 -1
- package/dist/chunk-2JDVJRR3.cjs.map +0 -1
- package/dist/chunk-3FKS4KQK.cjs.map +0 -1
- package/dist/chunk-45KP67RR.js.map +0 -1
- package/dist/chunk-B37IKTI7.cjs.map +0 -1
- package/dist/chunk-H2OEUBPO.js.map +0 -1
- package/dist/chunk-HDKDQAEQ.cjs.map +0 -1
- package/dist/chunk-K2SOC3XF.cjs.map +0 -1
- package/dist/chunk-KLJEK547.js.map +0 -1
- package/dist/chunk-MCFQAUQV.cjs.map +0 -1
- package/dist/chunk-MJGDP3CS.cjs.map +0 -1
- package/dist/chunk-NNU4RGT3.js.map +0 -1
- package/dist/chunk-OC22GGQN.js.map +0 -1
- package/dist/chunk-SQKLKBBK.cjs.map +0 -1
- package/dist/chunk-TYCHEOQX.js.map +0 -1
- package/dist/chunk-UGCUNADI.js.map +0 -1
- package/dist/chunk-VLMK7MQK.js.map +0 -1
- package/dist/chunk-XTATRNUN.cjs.map +0 -1
- package/dist/chunk-XZNPAD6E.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { parseLegendDirective, applyLegendOverrides, renderLegend } from './chunk-
|
|
1
|
+
import { parseLegendDirective, applyLegendOverrides, renderLegend } from './chunk-ZTSO3S4P.js';
|
|
2
2
|
import { resolveGenogramTheme, STROKE_WIDTH, cssCustomProperties } from './chunk-2VNMKOUO.js';
|
|
3
|
-
import { title, text, group, pattern, path, circle, el, defs, polygon, rect, line, desc, svgRoot } from './chunk-
|
|
3
|
+
import { title, text, group, pattern, path, circle, el, defs, polygon, rect, line, desc, svgRoot } from './chunk-WHJXRLFD.js';
|
|
4
4
|
|
|
5
5
|
// src/diagrams/genogram/parser.ts
|
|
6
6
|
var ParseError = class extends Error {
|
|
@@ -17,6 +17,9 @@ var ParseError = class extends Error {
|
|
|
17
17
|
source;
|
|
18
18
|
};
|
|
19
19
|
var COUPLE_OPS = [
|
|
20
|
+
{ token: "~/~", type: "cohabiting-ended" },
|
|
21
|
+
{ token: "-//", type: "separated" },
|
|
22
|
+
// alias for -/-
|
|
20
23
|
{ token: "-x-", type: "divorced" },
|
|
21
24
|
{ token: "-/-", type: "separated" },
|
|
22
25
|
{ token: "-o-", type: "engaged" },
|
|
@@ -37,6 +40,7 @@ var SPECIAL_CHILD_PROPS = /* @__PURE__ */ new Set([
|
|
|
37
40
|
"twin-identical",
|
|
38
41
|
"twin-fraternal"
|
|
39
42
|
]);
|
|
43
|
+
var SECONDARY_LINK_PROPS = /* @__PURE__ */ new Set(["foster", "adopted", "guardian"]);
|
|
40
44
|
var VALID_FILLS = /* @__PURE__ */ new Set([
|
|
41
45
|
"full",
|
|
42
46
|
"half-left",
|
|
@@ -110,6 +114,8 @@ function parseGenogram(text2) {
|
|
|
110
114
|
const relationships = [];
|
|
111
115
|
const childSpecialProps = /* @__PURE__ */ new Map();
|
|
112
116
|
const legendOverrides = {};
|
|
117
|
+
const childrenWithPrimary = /* @__PURE__ */ new Set();
|
|
118
|
+
let unknownSibCounter = 0;
|
|
113
119
|
skipBlankAndComments(state);
|
|
114
120
|
while (state.currentLine < state.lines.length) {
|
|
115
121
|
skipBlankAndComments(state);
|
|
@@ -164,8 +170,12 @@ function parseGenogram(text2) {
|
|
|
164
170
|
}
|
|
165
171
|
const rightKey = rightId.toLowerCase();
|
|
166
172
|
if (rightProps) {
|
|
167
|
-
const
|
|
168
|
-
individualsMap.
|
|
173
|
+
const incoming = buildIndividual(rightId, rightProps, lineNum, lineText);
|
|
174
|
+
const existing = individualsMap.get(rightKey);
|
|
175
|
+
individualsMap.set(
|
|
176
|
+
rightKey,
|
|
177
|
+
existing ? mergeIndividual(existing, incoming, lineNum, lineText) : incoming
|
|
178
|
+
);
|
|
169
179
|
} else if (!individualsMap.has(rightKey)) {
|
|
170
180
|
throw new ParseError(
|
|
171
181
|
`Unknown individual '${rightId}'`,
|
|
@@ -189,26 +199,47 @@ function parseGenogram(text2) {
|
|
|
189
199
|
const childIndent = getIndent(childLine);
|
|
190
200
|
if (childIndent <= coupleIndent) break;
|
|
191
201
|
const childLineNum = state.currentLine + 1;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
202
|
+
let parsedId;
|
|
203
|
+
let parsedProps;
|
|
204
|
+
if (childTrimmed === "?" || childTrimmed.startsWith("? ")) {
|
|
205
|
+
parsedId = `__unknown_siblings_${++unknownSibCounter}`;
|
|
206
|
+
parsedProps = "unknown, unknown-siblings";
|
|
207
|
+
} else {
|
|
208
|
+
const split = splitIdAndProps(childTrimmed);
|
|
209
|
+
parsedId = split.id;
|
|
210
|
+
parsedProps = split.propsStr;
|
|
211
|
+
}
|
|
212
|
+
const childKey = parsedId.toLowerCase();
|
|
213
|
+
const incoming = buildIndividual(
|
|
214
|
+
parsedId,
|
|
215
|
+
parsedProps,
|
|
197
216
|
childLineNum,
|
|
198
217
|
childLine
|
|
199
218
|
);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
childSpecialProps.set(childKey, sp);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
219
|
+
const tokens = parsedProps ? propsTokens(parsedProps) : [];
|
|
220
|
+
for (const sp of SPECIAL_CHILD_PROPS) {
|
|
221
|
+
if (tokens.includes(sp)) childSpecialProps.set(childKey, sp);
|
|
206
222
|
}
|
|
207
|
-
|
|
223
|
+
const isSecondaryDecl = tokens.some((t) => SECONDARY_LINK_PROPS.has(t));
|
|
224
|
+
const existing = individualsMap.get(childKey);
|
|
225
|
+
individualsMap.set(
|
|
226
|
+
childKey,
|
|
227
|
+
existing ? mergeIndividual(existing, incoming, childLineNum, childLine) : incoming
|
|
228
|
+
);
|
|
208
229
|
const coupleKey = `${leftKey}+${rightKey}`;
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
230
|
+
const lineChildType = tokens.find(
|
|
231
|
+
(t) => SPECIAL_CHILD_PROPS.has(t)
|
|
232
|
+
);
|
|
233
|
+
const relType = lineChildType === "adopted" || lineChildType === "foster" ? lineChildType : "parent-child";
|
|
234
|
+
const isSecondary = isSecondaryDecl && childrenWithPrimary.has(childKey);
|
|
235
|
+
const rel2 = {
|
|
236
|
+
type: relType,
|
|
237
|
+
from: coupleKey,
|
|
238
|
+
to: childKey
|
|
239
|
+
};
|
|
240
|
+
if (isSecondary) rel2.secondary = true;
|
|
241
|
+
relationships.push(rel2);
|
|
242
|
+
if (!isSecondary) childrenWithPrimary.add(childKey);
|
|
212
243
|
state.currentLine++;
|
|
213
244
|
}
|
|
214
245
|
} else {
|
|
@@ -218,7 +249,10 @@ function parseGenogram(text2) {
|
|
|
218
249
|
const individual = buildIndividual(id, propsStr, lineNum, lineText);
|
|
219
250
|
const existing = individualsMap.get(key);
|
|
220
251
|
if (existing) {
|
|
221
|
-
individualsMap.set(
|
|
252
|
+
individualsMap.set(
|
|
253
|
+
key,
|
|
254
|
+
mergeIndividual(existing, individual, lineNum, lineText)
|
|
255
|
+
);
|
|
222
256
|
} else {
|
|
223
257
|
individualsMap.set(key, individual);
|
|
224
258
|
}
|
|
@@ -249,6 +283,7 @@ function parseGenogram(text2) {
|
|
|
249
283
|
});
|
|
250
284
|
}
|
|
251
285
|
}
|
|
286
|
+
normalizePrimaryParentRels(relationships);
|
|
252
287
|
const hasLegendOverrides = Object.keys(legendOverrides).length > 0 && Object.values(legendOverrides).some((v) => v !== void 0);
|
|
253
288
|
return {
|
|
254
289
|
type: "genogram",
|
|
@@ -258,6 +293,33 @@ function parseGenogram(text2) {
|
|
|
258
293
|
legendOverrides: hasLegendOverrides ? legendOverrides : void 0
|
|
259
294
|
};
|
|
260
295
|
}
|
|
296
|
+
function normalizePrimaryParentRels(relationships) {
|
|
297
|
+
const PARENT_CHILD_TYPES = /* @__PURE__ */ new Set([
|
|
298
|
+
"parent-child",
|
|
299
|
+
"adopted",
|
|
300
|
+
"foster"
|
|
301
|
+
]);
|
|
302
|
+
const byChild = /* @__PURE__ */ new Map();
|
|
303
|
+
for (let i = 0; i < relationships.length; i++) {
|
|
304
|
+
const r = relationships[i];
|
|
305
|
+
if (!PARENT_CHILD_TYPES.has(r.type)) continue;
|
|
306
|
+
const arr = byChild.get(r.to) ?? [];
|
|
307
|
+
arr.push(i);
|
|
308
|
+
byChild.set(r.to, arr);
|
|
309
|
+
}
|
|
310
|
+
for (const indices of byChild.values()) {
|
|
311
|
+
if (indices.length < 2) continue;
|
|
312
|
+
let primaryIdx = indices.find((i) => relationships[i].type === "parent-child");
|
|
313
|
+
if (primaryIdx === void 0) primaryIdx = indices[0];
|
|
314
|
+
for (const i of indices) {
|
|
315
|
+
if (i === primaryIdx) {
|
|
316
|
+
delete relationships[i].secondary;
|
|
317
|
+
} else {
|
|
318
|
+
relationships[i].secondary = true;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
261
323
|
function currentLineText(state) {
|
|
262
324
|
return state.lines[state.currentLine];
|
|
263
325
|
}
|
|
@@ -389,7 +451,11 @@ function buildIndividual(id, propsStr, lineNum, lineText) {
|
|
|
389
451
|
} else if (tokenLower === "index") {
|
|
390
452
|
if (!individual.markers) individual.markers = [];
|
|
391
453
|
individual.markers.push("index-person");
|
|
392
|
-
} else if (
|
|
454
|
+
} else if (tokenLower === "unknown-siblings") {
|
|
455
|
+
if (!individual.markers) individual.markers = [];
|
|
456
|
+
individual.markers.push("unknown-siblings");
|
|
457
|
+
if (!individual.label || individual.label === id) individual.label = "?";
|
|
458
|
+
} else if (SPECIAL_CHILD_PROPS.has(tokenLower) || tokenLower === "guardian") {
|
|
393
459
|
continue;
|
|
394
460
|
} else if (/^\d{4}$/.test(tokenLower)) {
|
|
395
461
|
if (individual.birthYear !== void 0) {
|
|
@@ -415,13 +481,27 @@ function buildIndividual(id, propsStr, lineNum, lineText) {
|
|
|
415
481
|
if (!isNaN(deathNum)) individual.deathYear = deathNum;
|
|
416
482
|
} else if (key === "label") {
|
|
417
483
|
individual.label = value.replace(/^"|"$/g, "");
|
|
484
|
+
} else if (key === "sibling-of") {
|
|
485
|
+
individual.siblingOf = value.toLowerCase();
|
|
486
|
+
} else if (key === "shape") {
|
|
487
|
+
const v = value.toLowerCase();
|
|
488
|
+
if (v === "square" || v === "circle" || v === "diamond" || v === "triangle" || v === "triangle-down") {
|
|
489
|
+
individual.shape = v;
|
|
490
|
+
} else {
|
|
491
|
+
throw new ParseError(
|
|
492
|
+
`Invalid shape '${value}'. Valid: square, circle, diamond, triangle, triangle-down`,
|
|
493
|
+
lineNum,
|
|
494
|
+
1,
|
|
495
|
+
lineText
|
|
496
|
+
);
|
|
497
|
+
}
|
|
418
498
|
} else {
|
|
419
499
|
if (!individual.properties) individual.properties = {};
|
|
420
500
|
individual.properties[key] = value;
|
|
421
501
|
}
|
|
422
502
|
} else {
|
|
423
503
|
throw new ParseError(
|
|
424
|
-
`Unknown property '${token}'. Valid: male, female, unknown, deceased, stillborn, miscarriage, abortion, adopted, foster, twin-identical, twin-fraternal, index, a 4-digit year, conditions:..., age:N, death:YYYY, or key:value`,
|
|
504
|
+
`Unknown property '${token}'. Valid: male, female, unknown, deceased, stillborn, miscarriage, abortion, adopted, foster, guardian, twin-identical, twin-fraternal, index, unknown-siblings, a 4-digit year, conditions:..., age:N, death:YYYY, label:"...", sibling-of:ID, or key:value`,
|
|
425
505
|
lineNum,
|
|
426
506
|
1,
|
|
427
507
|
lineText
|
|
@@ -461,20 +541,81 @@ function parseConditions(raw, lineNum, lineText) {
|
|
|
461
541
|
}
|
|
462
542
|
return conditions;
|
|
463
543
|
}
|
|
464
|
-
function mergeIndividual(existing, incoming) {
|
|
544
|
+
function mergeIndividual(existing, incoming, lineNum, lineText) {
|
|
545
|
+
let mergedSex = existing.sex;
|
|
546
|
+
if (incoming.sex !== "unknown") {
|
|
547
|
+
if (existing.sex !== "unknown" && existing.sex !== incoming.sex) {
|
|
548
|
+
throw new ParseError(
|
|
549
|
+
`Conflicting sex for '${existing.id}': previously '${existing.sex}', now '${incoming.sex}'`,
|
|
550
|
+
lineNum,
|
|
551
|
+
1,
|
|
552
|
+
lineText
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
mergedSex = incoming.sex;
|
|
556
|
+
}
|
|
557
|
+
let mergedStatus = existing.status;
|
|
558
|
+
if (incoming.status !== "alive") {
|
|
559
|
+
if (existing.status !== "alive" && existing.status !== incoming.status) {
|
|
560
|
+
throw new ParseError(
|
|
561
|
+
`Conflicting status for '${existing.id}': previously '${existing.status}', now '${incoming.status}'`,
|
|
562
|
+
lineNum,
|
|
563
|
+
1,
|
|
564
|
+
lineText
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
mergedStatus = incoming.status;
|
|
568
|
+
}
|
|
569
|
+
if (incoming.birthYear !== void 0 && existing.birthYear !== void 0 && incoming.birthYear !== existing.birthYear) {
|
|
570
|
+
throw new ParseError(
|
|
571
|
+
`Conflicting birth year for '${existing.id}': previously ${existing.birthYear}, now ${incoming.birthYear}`,
|
|
572
|
+
lineNum,
|
|
573
|
+
1,
|
|
574
|
+
lineText
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
if (incoming.deathYear !== void 0 && existing.deathYear !== void 0 && incoming.deathYear !== existing.deathYear) {
|
|
578
|
+
throw new ParseError(
|
|
579
|
+
`Conflicting death year for '${existing.id}': previously ${existing.deathYear}, now ${incoming.deathYear}`,
|
|
580
|
+
lineNum,
|
|
581
|
+
1,
|
|
582
|
+
lineText
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
const incomingHasExplicitLabel = incoming.label !== incoming.id && incoming.label !== "";
|
|
586
|
+
const existingHasExplicitLabel = existing.label !== existing.id && existing.label !== "";
|
|
587
|
+
const mergedLabel = incomingHasExplicitLabel ? incoming.label : existingHasExplicitLabel ? existing.label : existing.label;
|
|
588
|
+
const mergedMarkers = mergeArrayUnique(existing.markers, incoming.markers);
|
|
465
589
|
return {
|
|
466
590
|
...existing,
|
|
467
|
-
sex:
|
|
468
|
-
status:
|
|
591
|
+
sex: mergedSex,
|
|
592
|
+
status: mergedStatus,
|
|
593
|
+
label: mergedLabel,
|
|
469
594
|
birthYear: incoming.birthYear ?? existing.birthYear,
|
|
470
595
|
deathYear: incoming.deathYear ?? existing.deathYear,
|
|
596
|
+
age: incoming.age ?? existing.age,
|
|
471
597
|
conditions: incoming.conditions ?? existing.conditions,
|
|
598
|
+
heritage: incoming.heritage ?? existing.heritage,
|
|
599
|
+
siblingOf: incoming.siblingOf ?? existing.siblingOf,
|
|
600
|
+
markers: mergedMarkers,
|
|
472
601
|
properties: {
|
|
473
602
|
...existing.properties,
|
|
474
603
|
...incoming.properties
|
|
475
604
|
}
|
|
476
605
|
};
|
|
477
606
|
}
|
|
607
|
+
function mergeArrayUnique(a, b) {
|
|
608
|
+
if (!a && !b) return void 0;
|
|
609
|
+
const seen = /* @__PURE__ */ new Set();
|
|
610
|
+
const out = [];
|
|
611
|
+
for (const v of [...a ?? [], ...b ?? []]) {
|
|
612
|
+
if (!seen.has(v)) {
|
|
613
|
+
seen.add(v);
|
|
614
|
+
out.push(v);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return out.length ? out : void 0;
|
|
618
|
+
}
|
|
478
619
|
|
|
479
620
|
// src/diagrams/genogram/layout.ts
|
|
480
621
|
function layoutGenogram(ast, config) {
|
|
@@ -483,8 +624,19 @@ function layoutGenogram(ast, config) {
|
|
|
483
624
|
const ordered = orderNodesInGenerations(graph);
|
|
484
625
|
const positions = assignPositions(ordered, graph, config);
|
|
485
626
|
const edges = computeEdges(graph, positions, config);
|
|
627
|
+
const secondaryEdges = computeSecondaryParentEdges(
|
|
628
|
+
ast.relationships,
|
|
629
|
+
graph,
|
|
630
|
+
positions,
|
|
631
|
+
config
|
|
632
|
+
);
|
|
486
633
|
const emotionalEdges = computeEmotionalEdges(ast.relationships, positions, config);
|
|
487
|
-
return packageResult(
|
|
634
|
+
return packageResult(
|
|
635
|
+
positions,
|
|
636
|
+
[...edges, ...secondaryEdges, ...emotionalEdges],
|
|
637
|
+
graph,
|
|
638
|
+
config
|
|
639
|
+
);
|
|
488
640
|
}
|
|
489
641
|
function buildGraph(ast) {
|
|
490
642
|
const individuals = /* @__PURE__ */ new Map();
|
|
@@ -494,7 +646,7 @@ function buildGraph(ast) {
|
|
|
494
646
|
const familyUnits = [];
|
|
495
647
|
const childOf = /* @__PURE__ */ new Map();
|
|
496
648
|
const coupleRels = ast.relationships.filter(
|
|
497
|
-
(r) => r.type === "married" || r.type === "divorced" || r.type === "separated" || r.type === "engaged" || r.type === "cohabiting" || r.type === "consanguineous"
|
|
649
|
+
(r) => r.type === "married" || r.type === "divorced" || r.type === "separated" || r.type === "engaged" || r.type === "cohabiting" || r.type === "cohabiting-ended" || r.type === "consanguineous"
|
|
498
650
|
);
|
|
499
651
|
for (const rel of coupleRels) {
|
|
500
652
|
const fuId = `${rel.from}+${rel.to}`;
|
|
@@ -516,7 +668,7 @@ function buildGraph(ast) {
|
|
|
516
668
|
}
|
|
517
669
|
const children = [];
|
|
518
670
|
for (const r of ast.relationships) {
|
|
519
|
-
if ((r.type === "parent-child" || r.type === "adopted" || r.type === "foster") && r.from === fuId) {
|
|
671
|
+
if ((r.type === "parent-child" || r.type === "adopted" || r.type === "foster") && r.from === fuId && !r.secondary) {
|
|
520
672
|
children.push(r.to);
|
|
521
673
|
childOf.set(r.to, fuId);
|
|
522
674
|
}
|
|
@@ -539,7 +691,12 @@ function buildGraph(ast) {
|
|
|
539
691
|
function assignGenerations(graph) {
|
|
540
692
|
const { individuals, familyUnits, childOf, generations } = graph;
|
|
541
693
|
const allIds = Array.from(individuals.keys());
|
|
542
|
-
const roots = allIds.filter((id) =>
|
|
694
|
+
const roots = allIds.filter((id) => {
|
|
695
|
+
if (childOf.has(id)) return false;
|
|
696
|
+
const ind = individuals.get(id);
|
|
697
|
+
if (ind?.siblingOf) return false;
|
|
698
|
+
return true;
|
|
699
|
+
});
|
|
543
700
|
if (roots.length === 0 && allIds.length > 0) {
|
|
544
701
|
for (const id of allIds) generations.set(id, 0);
|
|
545
702
|
return;
|
|
@@ -587,6 +744,21 @@ function assignGenerations(graph) {
|
|
|
587
744
|
}
|
|
588
745
|
}
|
|
589
746
|
}
|
|
747
|
+
let siblingChanged = true;
|
|
748
|
+
let safety = 0;
|
|
749
|
+
while (siblingChanged && safety++ < allIds.length + 1) {
|
|
750
|
+
siblingChanged = false;
|
|
751
|
+
for (const id of allIds) {
|
|
752
|
+
if (generations.has(id)) continue;
|
|
753
|
+
const ind = individuals.get(id);
|
|
754
|
+
if (!ind?.siblingOf) continue;
|
|
755
|
+
const refGen = generations.get(ind.siblingOf);
|
|
756
|
+
if (refGen !== void 0) {
|
|
757
|
+
generations.set(id, refGen);
|
|
758
|
+
siblingChanged = true;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
590
762
|
for (const id of allIds) {
|
|
591
763
|
if (!generations.has(id)) {
|
|
592
764
|
generations.set(id, 0);
|
|
@@ -662,15 +834,33 @@ function orderGeneration(nodeIds, _genIdx, graph, familyUnits) {
|
|
|
662
834
|
}
|
|
663
835
|
}
|
|
664
836
|
const remaining = nodeIds.filter((id) => !placed.has(id));
|
|
665
|
-
remaining.
|
|
837
|
+
const siblingOfRemaining = remaining.filter((id) => {
|
|
838
|
+
const ind = graph.individuals.get(id);
|
|
839
|
+
return ind?.siblingOf && placed.has(ind.siblingOf);
|
|
840
|
+
});
|
|
841
|
+
const otherRemaining = remaining.filter(
|
|
842
|
+
(id) => !siblingOfRemaining.includes(id)
|
|
843
|
+
);
|
|
844
|
+
otherRemaining.sort((a, b) => {
|
|
666
845
|
const indA = graph.individuals.get(a);
|
|
667
846
|
const indB = graph.individuals.get(b);
|
|
668
847
|
return (indA?.birthYear ?? 9999) - (indB?.birthYear ?? 9999);
|
|
669
848
|
});
|
|
670
|
-
for (const id of
|
|
849
|
+
for (const id of otherRemaining) {
|
|
671
850
|
ordered.push(id);
|
|
672
851
|
placed.add(id);
|
|
673
852
|
}
|
|
853
|
+
for (const id of siblingOfRemaining) {
|
|
854
|
+
const ind = graph.individuals.get(id);
|
|
855
|
+
if (!ind?.siblingOf) continue;
|
|
856
|
+
const refIdx = ordered.indexOf(ind.siblingOf);
|
|
857
|
+
if (refIdx === -1) {
|
|
858
|
+
ordered.push(id);
|
|
859
|
+
} else {
|
|
860
|
+
ordered.splice(refIdx + 1, 0, id);
|
|
861
|
+
}
|
|
862
|
+
placed.add(id);
|
|
863
|
+
}
|
|
674
864
|
return ordered;
|
|
675
865
|
}
|
|
676
866
|
var LABEL_HEIGHT = 20;
|
|
@@ -698,9 +888,10 @@ function assignPositions(orderedGens, graph, config) {
|
|
|
698
888
|
}
|
|
699
889
|
}
|
|
700
890
|
}
|
|
701
|
-
resolveOverlaps(positions, orderedGens, config);
|
|
891
|
+
resolveOverlaps(positions, orderedGens, config, graph);
|
|
702
892
|
centerChildrenUnderParents(positions, graph, config);
|
|
703
|
-
|
|
893
|
+
unscrambleSibships(positions, graph, config);
|
|
894
|
+
resolveOverlaps(positions, orderedGens, config, graph);
|
|
704
895
|
return positions;
|
|
705
896
|
}
|
|
706
897
|
function buildSegments(nodeIds, _genIdx, graph) {
|
|
@@ -848,15 +1039,81 @@ function centerChildrenUnderParents(positions, graph, config) {
|
|
|
848
1039
|
}
|
|
849
1040
|
}
|
|
850
1041
|
}
|
|
851
|
-
function
|
|
1042
|
+
function unscrambleSibships(positions, graph, config) {
|
|
1043
|
+
const familyGap = config.nodeWidth + config.nodeSpacingX * 1.5;
|
|
1044
|
+
const childSpacing = config.nodeWidth + config.nodeSpacingX;
|
|
1045
|
+
const byGen = /* @__PURE__ */ new Map();
|
|
1046
|
+
for (const [id, pos] of positions) {
|
|
1047
|
+
const arr = byGen.get(pos.generation) ?? [];
|
|
1048
|
+
arr.push(id);
|
|
1049
|
+
byGen.set(pos.generation, arr);
|
|
1050
|
+
}
|
|
1051
|
+
for (const [, ids] of byGen) {
|
|
1052
|
+
const sibshipMap = /* @__PURE__ */ new Map();
|
|
1053
|
+
for (const id of ids) {
|
|
1054
|
+
const fu = graph.childOf.get(id);
|
|
1055
|
+
if (!fu) continue;
|
|
1056
|
+
const arr = sibshipMap.get(fu) ?? [];
|
|
1057
|
+
arr.push(id);
|
|
1058
|
+
sibshipMap.set(fu, arr);
|
|
1059
|
+
}
|
|
1060
|
+
if (sibshipMap.size <= 1) continue;
|
|
1061
|
+
const sibships = [...sibshipMap.entries()].map(([fuId, children]) => {
|
|
1062
|
+
const fu = graph.familyUnits.find((f) => f.id === fuId);
|
|
1063
|
+
if (!fu) return null;
|
|
1064
|
+
const pa = positions.get(fu.partners[0]);
|
|
1065
|
+
const pb = positions.get(fu.partners[1]);
|
|
1066
|
+
if (!pa || !pb) return null;
|
|
1067
|
+
return { fuId, children, midX: (pa.x + pb.x) / 2 };
|
|
1068
|
+
}).filter((x) => x !== null).sort((a, b) => a.midX - b.midX);
|
|
1069
|
+
let cursor = null;
|
|
1070
|
+
for (const ss of sibships) {
|
|
1071
|
+
const sortedKids = [...ss.children].sort((a, b) => {
|
|
1072
|
+
const ya = graph.individuals.get(a)?.birthYear ?? 9999;
|
|
1073
|
+
const yb = graph.individuals.get(b)?.birthYear ?? 9999;
|
|
1074
|
+
return ya - yb;
|
|
1075
|
+
});
|
|
1076
|
+
const totalWidth = Math.max(0, (sortedKids.length - 1) * childSpacing);
|
|
1077
|
+
let startX = ss.midX - totalWidth / 2;
|
|
1078
|
+
if (cursor !== null && startX < cursor) startX = cursor;
|
|
1079
|
+
for (let i = 0; i < sortedKids.length; i++) {
|
|
1080
|
+
const pos = positions.get(sortedKids[i]);
|
|
1081
|
+
if (pos) pos.x = startX + i * childSpacing;
|
|
1082
|
+
}
|
|
1083
|
+
cursor = startX + totalWidth + familyGap;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
function resolveOverlaps(positions, orderedGens, config, graph) {
|
|
852
1088
|
const minGap = config.nodeWidth + config.nodeSpacingX;
|
|
1089
|
+
const familyGap = config.nodeWidth + config.nodeSpacingX * 1.5;
|
|
1090
|
+
const cluster = /* @__PURE__ */ new Map();
|
|
1091
|
+
if (graph) {
|
|
1092
|
+
for (const [id] of positions) {
|
|
1093
|
+
const fu = graph.childOf.get(id);
|
|
1094
|
+
if (fu) cluster.set(id, fu);
|
|
1095
|
+
}
|
|
1096
|
+
for (const fu of graph.familyUnits) {
|
|
1097
|
+
for (const p of fu.partners) {
|
|
1098
|
+
if (cluster.has(p)) continue;
|
|
1099
|
+
const other = fu.partners[0] === p ? fu.partners[1] : fu.partners[0];
|
|
1100
|
+
const otherCluster = cluster.get(other);
|
|
1101
|
+
if (otherCluster) cluster.set(p, otherCluster);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
853
1105
|
for (const gen of orderedGens) {
|
|
854
1106
|
const genNodes = gen.nodeIds.map((id) => positions.get(id)).filter((p) => p !== void 0);
|
|
855
1107
|
genNodes.sort((a, b) => a.x - b.x);
|
|
856
1108
|
for (let i = 1; i < genNodes.length; i++) {
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
1109
|
+
const prev = genNodes[i - 1];
|
|
1110
|
+
const cur = genNodes[i];
|
|
1111
|
+
const cPrev = cluster.get(prev.id);
|
|
1112
|
+
const cCur = cluster.get(cur.id);
|
|
1113
|
+
const required = cPrev && cCur && cPrev !== cCur ? familyGap : minGap;
|
|
1114
|
+
const gap = cur.x - prev.x;
|
|
1115
|
+
if (gap < required) {
|
|
1116
|
+
const shift = required - gap;
|
|
860
1117
|
for (let j = i; j < genNodes.length; j++) {
|
|
861
1118
|
genNodes[j].x += shift;
|
|
862
1119
|
}
|
|
@@ -1089,6 +1346,34 @@ function computeEmotionalEdges(relationships, positions, config) {
|
|
|
1089
1346
|
}
|
|
1090
1347
|
return edges;
|
|
1091
1348
|
}
|
|
1349
|
+
function computeSecondaryParentEdges(relationships, graph, positions, config) {
|
|
1350
|
+
const edges = [];
|
|
1351
|
+
const half = config.nodeHeight / 2;
|
|
1352
|
+
for (const rel of relationships) {
|
|
1353
|
+
if (!rel.secondary) continue;
|
|
1354
|
+
if (rel.type !== "parent-child" && rel.type !== "foster" && rel.type !== "adopted")
|
|
1355
|
+
continue;
|
|
1356
|
+
const fu = graph.familyUnits.find((f) => f.id === rel.from);
|
|
1357
|
+
if (!fu) continue;
|
|
1358
|
+
const posA = positions.get(fu.partners[0]);
|
|
1359
|
+
const posB = positions.get(fu.partners[1]);
|
|
1360
|
+
const childPos = positions.get(rel.to);
|
|
1361
|
+
if (!posA || !posB || !childPos) continue;
|
|
1362
|
+
const coupleMidX = (posA.x + posB.x) / 2;
|
|
1363
|
+
const coupleY = posA.y;
|
|
1364
|
+
const startX = coupleMidX;
|
|
1365
|
+
const startY = coupleY + half + 4;
|
|
1366
|
+
const childTopY = childPos.y - half - 4;
|
|
1367
|
+
const path2 = childPos.y === coupleY ? `M ${startX} ${startY} L ${startX} ${startY + 16} L ${childPos.x} ${startY + 16} L ${childPos.x} ${childPos.y - half - 4}` : `M ${startX} ${startY} L ${startX} ${(startY + childTopY) / 2} L ${childPos.x} ${(startY + childTopY) / 2} L ${childPos.x} ${childTopY}`;
|
|
1368
|
+
edges.push({
|
|
1369
|
+
from: rel.from,
|
|
1370
|
+
to: rel.to,
|
|
1371
|
+
relationship: rel,
|
|
1372
|
+
path: path2
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
return edges;
|
|
1376
|
+
}
|
|
1092
1377
|
function shiftPath(pathData, dx, dy) {
|
|
1093
1378
|
return pathData.replace(
|
|
1094
1379
|
/([\d.-]+)\s+([\d.-]+)/g,
|
|
@@ -1115,7 +1400,7 @@ function renderIndividualSymbol(individual, x, y, size) {
|
|
|
1115
1400
|
children.push(indexBorder(individual.sex, half));
|
|
1116
1401
|
classes.push("schematex-genogram-index-person");
|
|
1117
1402
|
}
|
|
1118
|
-
children.push(baseShape(individual.sex, half));
|
|
1403
|
+
children.push(baseShape(individual.sex, half, individual.shape));
|
|
1119
1404
|
if (individual.conditions?.length) {
|
|
1120
1405
|
for (const cond of individual.conditions) {
|
|
1121
1406
|
children.push(conditionFillElement(individual.sex, half, cond));
|
|
@@ -1139,6 +1424,23 @@ function renderIndividualSymbol(individual, x, y, size) {
|
|
|
1139
1424
|
)
|
|
1140
1425
|
);
|
|
1141
1426
|
}
|
|
1427
|
+
const isUnknownSiblings = individual.markers?.includes("unknown-siblings");
|
|
1428
|
+
if (isUnknownSiblings) {
|
|
1429
|
+
classes.push("schematex-genogram-unknown-siblings");
|
|
1430
|
+
children.push(
|
|
1431
|
+
text(
|
|
1432
|
+
{
|
|
1433
|
+
x: 0,
|
|
1434
|
+
y: 5,
|
|
1435
|
+
class: "schematex-genogram-unknown-siblings-mark",
|
|
1436
|
+
"text-anchor": "middle",
|
|
1437
|
+
"font-size": "16",
|
|
1438
|
+
"font-weight": "bold"
|
|
1439
|
+
},
|
|
1440
|
+
"?"
|
|
1441
|
+
)
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1142
1444
|
return group(
|
|
1143
1445
|
{
|
|
1144
1446
|
class: classes.join(" "),
|
|
@@ -1280,31 +1582,30 @@ function indexBorder(sex, half) {
|
|
|
1280
1582
|
});
|
|
1281
1583
|
}
|
|
1282
1584
|
}
|
|
1283
|
-
function baseShape(sex, half) {
|
|
1585
|
+
function baseShape(sex, half, shape) {
|
|
1586
|
+
const cls = "schematex-genogram-shape";
|
|
1587
|
+
switch (shape) {
|
|
1588
|
+
case "square":
|
|
1589
|
+
return rect({ x: -half, y: -half, width: half * 2, height: half * 2, class: cls });
|
|
1590
|
+
case "circle":
|
|
1591
|
+
return circle({ cx: 0, cy: 0, r: half, class: cls });
|
|
1592
|
+
case "diamond":
|
|
1593
|
+
return polygon({ points: `0,${-half} ${half},0 0,${half} ${-half},0`, class: cls });
|
|
1594
|
+
case "triangle":
|
|
1595
|
+
return polygon({ points: `0,${-half} ${half},${half} ${-half},${half}`, class: cls });
|
|
1596
|
+
case "triangle-down":
|
|
1597
|
+
return polygon({ points: `${-half},${-half} ${half},${-half} 0,${half}`, class: cls });
|
|
1598
|
+
}
|
|
1284
1599
|
switch (sex) {
|
|
1285
1600
|
case "male":
|
|
1286
|
-
return rect({
|
|
1287
|
-
x: -half,
|
|
1288
|
-
y: -half,
|
|
1289
|
-
width: half * 2,
|
|
1290
|
-
height: half * 2,
|
|
1291
|
-
class: "schematex-genogram-shape"
|
|
1292
|
-
});
|
|
1601
|
+
return rect({ x: -half, y: -half, width: half * 2, height: half * 2, class: cls });
|
|
1293
1602
|
case "female":
|
|
1294
|
-
return circle({
|
|
1295
|
-
cx: 0,
|
|
1296
|
-
cy: 0,
|
|
1297
|
-
r: half,
|
|
1298
|
-
class: "schematex-genogram-shape"
|
|
1299
|
-
});
|
|
1603
|
+
return circle({ cx: 0, cy: 0, r: half, class: cls });
|
|
1300
1604
|
case "unknown":
|
|
1301
1605
|
case "other":
|
|
1302
1606
|
case "nonbinary":
|
|
1303
1607
|
case "intersex":
|
|
1304
|
-
return polygon({
|
|
1305
|
-
points: `0,${-half} ${half},0 0,${half} ${-half},0`,
|
|
1306
|
-
class: "schematex-genogram-shape"
|
|
1307
|
-
});
|
|
1608
|
+
return polygon({ points: `0,${-half} ${half},0 0,${half} ${-half},0`, class: cls });
|
|
1308
1609
|
}
|
|
1309
1610
|
}
|
|
1310
1611
|
function deceasedOverlay(sex, half) {
|
|
@@ -1401,6 +1702,7 @@ var STRUCTURAL_TYPES = /* @__PURE__ */ new Set([
|
|
|
1401
1702
|
"separated",
|
|
1402
1703
|
"engaged",
|
|
1403
1704
|
"cohabiting",
|
|
1705
|
+
"cohabiting-ended",
|
|
1404
1706
|
"domestic-partnership",
|
|
1405
1707
|
"consanguineous",
|
|
1406
1708
|
"parent-child",
|
|
@@ -1593,6 +1895,7 @@ function buildStructuralItems(rels, theme) {
|
|
|
1593
1895
|
"separated",
|
|
1594
1896
|
"engaged",
|
|
1595
1897
|
"cohabiting",
|
|
1898
|
+
"cohabiting-ended",
|
|
1596
1899
|
"domestic-partnership",
|
|
1597
1900
|
"consanguineous",
|
|
1598
1901
|
"adopted",
|
|
@@ -1622,6 +1925,14 @@ function structuralItem(t, theme) {
|
|
|
1622
1925
|
return { ...base, kind: "edge", marker: "X", pattern: "solid" };
|
|
1623
1926
|
case "separated":
|
|
1624
1927
|
return { ...base, kind: "edge", marker: "slash", pattern: "solid" };
|
|
1928
|
+
case "cohabiting-ended":
|
|
1929
|
+
return {
|
|
1930
|
+
...base,
|
|
1931
|
+
label: "Cohabiting (ended)",
|
|
1932
|
+
kind: "edge",
|
|
1933
|
+
marker: "slash",
|
|
1934
|
+
pattern: "dashed"
|
|
1935
|
+
};
|
|
1625
1936
|
case "engaged":
|
|
1626
1937
|
case "cohabiting":
|
|
1627
1938
|
case "adopted":
|
|
@@ -1784,7 +2095,8 @@ function buildMarkerItems(individuals, theme) {
|
|
|
1784
2095
|
"index-person",
|
|
1785
2096
|
"transgender",
|
|
1786
2097
|
"no-children",
|
|
1787
|
-
"infertile"
|
|
2098
|
+
"infertile",
|
|
2099
|
+
"unknown-siblings"
|
|
1788
2100
|
];
|
|
1789
2101
|
const items = [];
|
|
1790
2102
|
for (const m of order) {
|
|
@@ -1817,6 +2129,16 @@ function markerItem(m, theme) {
|
|
|
1817
2129
|
return { key: `marker.${m}`, label: "No children (by choice)", kind: "marker", marker: "slash", section: "markers" };
|
|
1818
2130
|
case "infertile":
|
|
1819
2131
|
return { key: `marker.${m}`, label: "Infertile", kind: "marker", marker: "X", section: "markers" };
|
|
2132
|
+
case "unknown-siblings":
|
|
2133
|
+
return {
|
|
2134
|
+
key: `marker.${m}`,
|
|
2135
|
+
label: "Sibling(s) \u2014 unknown count",
|
|
2136
|
+
kind: "shape",
|
|
2137
|
+
shape: "diamond",
|
|
2138
|
+
fill: "#ffffff",
|
|
2139
|
+
color: theme?.stroke,
|
|
2140
|
+
section: "markers"
|
|
2141
|
+
};
|
|
1820
2142
|
default:
|
|
1821
2143
|
return { key: `marker.${m}`, label: humanize(m), kind: "marker", marker: "dot", section: "markers" };
|
|
1822
2144
|
}
|
|
@@ -1837,7 +2159,11 @@ function renderGenogram(layout, config, ast) {
|
|
|
1837
2159
|
const emotionalLayer = renderEmotionalEdges(emotionalEdges);
|
|
1838
2160
|
const nodeLayers = renderNodes(genGroups);
|
|
1839
2161
|
const labelLayer = renderLabels(layout.nodes, config);
|
|
1840
|
-
const
|
|
2162
|
+
const labelEdges = structuralEdges.filter(
|
|
2163
|
+
(e) => !e.relationship.secondary
|
|
2164
|
+
);
|
|
2165
|
+
const edgeLabelLayer = renderEdgeLabels(labelEdges, config);
|
|
2166
|
+
const siblingOfLayer = renderSiblingOfBrackets(layout, ast);
|
|
1841
2167
|
const nodeCount = layout.nodes.length;
|
|
1842
2168
|
const genCount = genGroups.size;
|
|
1843
2169
|
const chartTitle = ast?.metadata?.title;
|
|
@@ -1871,7 +2197,7 @@ function renderGenogram(layout, config, ast) {
|
|
|
1871
2197
|
chartContent.push(
|
|
1872
2198
|
group(
|
|
1873
2199
|
{ transform: titleHeight > 0 ? `translate(0, ${titleHeight})` : void 0 },
|
|
1874
|
-
[edgeLayers, emotionalLayer, ...nodeLayers, labelLayer, edgeLabelLayer]
|
|
2200
|
+
[edgeLayers, siblingOfLayer, emotionalLayer, ...nodeLayers, labelLayer, edgeLabelLayer]
|
|
1875
2201
|
)
|
|
1876
2202
|
);
|
|
1877
2203
|
let finalWidth = layout.width;
|
|
@@ -1933,8 +2259,15 @@ function buildStyles(config) {
|
|
|
1933
2259
|
.schematex-genogram-label { font-family: ${config.fontFamily}; font-size: ${config.fontSize}px; text-anchor: middle; fill: ${t.text}; }
|
|
1934
2260
|
.schematex-genogram-edge { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
|
1935
2261
|
.schematex-genogram-edge-cohabiting path { stroke-dasharray: 6,4; }
|
|
2262
|
+
.schematex-genogram-edge-cohabiting-ended path { stroke-dasharray: 6,4; }
|
|
1936
2263
|
.schematex-genogram-edge-divorced .schematex-genogram-divorce-mark { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; }
|
|
1937
2264
|
.schematex-genogram-edge-separated .schematex-genogram-separation-mark { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; }
|
|
2265
|
+
.schematex-genogram-edge-cohabiting-ended .schematex-genogram-separation-mark { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; }
|
|
2266
|
+
/* Secondary parent-child link (foster/adopted "current caregiver") \u2014 dotted, muted */
|
|
2267
|
+
.schematex-genogram-edge-secondary path { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; stroke-dasharray: 2,4; fill: none; opacity: 0.85; }
|
|
2268
|
+
/* Sibling-of bracket (known relative, unknown ancestry) \u2014 dashed */
|
|
2269
|
+
.schematex-genogram-sibling-of path { stroke: ${t.neutral}; stroke-width: ${STROKE_WIDTH.normal}; stroke-dasharray: 4,3; fill: none; opacity: 0.7; }
|
|
2270
|
+
.schematex-genogram-unknown-siblings-mark { fill: ${t.text}; pointer-events: none; }
|
|
1938
2271
|
.schematex-genogram-deceased-mark { stroke: ${t.deceasedMark}; stroke-width: ${STROKE_WIDTH.normal}; stroke-linecap: round; }
|
|
1939
2272
|
/* Inline fill on each .schematex-genogram-condition-fill element comes from cond.color. The CSS only sets a default for elements that did not receive an inline fill attribute. */
|
|
1940
2273
|
.schematex-genogram-condition-fill:not([fill]) { fill: ${t.conditionFill}; }
|
|
@@ -2080,10 +2413,27 @@ function renderEdges(edges) {
|
|
|
2080
2413
|
const children = [];
|
|
2081
2414
|
for (const edge of edges) {
|
|
2082
2415
|
const relType = edge.relationship.type;
|
|
2083
|
-
const
|
|
2416
|
+
const isSecondary = edge.relationship.secondary === true;
|
|
2417
|
+
const cssClass = isSecondary ? `schematex-genogram-edge schematex-genogram-edge-secondary schematex-genogram-edge-secondary-${relType}` : `schematex-genogram-edge schematex-genogram-edge-${relType}`;
|
|
2084
2418
|
const elements = [
|
|
2085
2419
|
el("path", { d: edge.path, class: "schematex-genogram-edge-path" })
|
|
2086
2420
|
];
|
|
2421
|
+
if (relType === "cohabiting-ended" && !isSecondary) {
|
|
2422
|
+
const mid = pathMidpoint(edge.path);
|
|
2423
|
+
if (mid) {
|
|
2424
|
+
elements.push(
|
|
2425
|
+
el("line", {
|
|
2426
|
+
x1: mid.x - 4,
|
|
2427
|
+
y1: mid.y - 6,
|
|
2428
|
+
x2: mid.x + 4,
|
|
2429
|
+
y2: mid.y + 6,
|
|
2430
|
+
class: "schematex-genogram-separation-mark",
|
|
2431
|
+
stroke: "#333",
|
|
2432
|
+
"stroke-width": "2"
|
|
2433
|
+
})
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2087
2437
|
if (relType === "divorced") {
|
|
2088
2438
|
const mid = pathMidpoint(edge.path);
|
|
2089
2439
|
if (mid) {
|
|
@@ -2182,6 +2532,35 @@ function renderNodes(genGroups) {
|
|
|
2182
2532
|
}
|
|
2183
2533
|
return layers;
|
|
2184
2534
|
}
|
|
2535
|
+
function renderSiblingOfBrackets(layout, ast) {
|
|
2536
|
+
if (!ast) return group({ class: "schematex-genogram-sibling-of-edges" }, []);
|
|
2537
|
+
const elements = [];
|
|
2538
|
+
const nodeById = new Map(layout.nodes.map((n) => [n.id, n]));
|
|
2539
|
+
for (const ind of ast.individuals) {
|
|
2540
|
+
if (!ind.siblingOf) continue;
|
|
2541
|
+
const fromNode = nodeById.get(ind.id);
|
|
2542
|
+
const toNode = nodeById.get(ind.siblingOf);
|
|
2543
|
+
if (!fromNode || !toNode) continue;
|
|
2544
|
+
if (fromNode.generation !== toNode.generation) continue;
|
|
2545
|
+
const fromCx = fromNode.x + fromNode.width / 2;
|
|
2546
|
+
const fromTopY = fromNode.y;
|
|
2547
|
+
const toCx = toNode.x + toNode.width / 2;
|
|
2548
|
+
const toTopY = toNode.y;
|
|
2549
|
+
const bracketY = Math.min(fromTopY, toTopY) - 12;
|
|
2550
|
+
const path2 = `M ${fromCx} ${fromTopY} L ${fromCx} ${bracketY} L ${toCx} ${bracketY} L ${toCx} ${toTopY}`;
|
|
2551
|
+
elements.push(
|
|
2552
|
+
group(
|
|
2553
|
+
{
|
|
2554
|
+
class: "schematex-genogram-sibling-of",
|
|
2555
|
+
"data-from": ind.id,
|
|
2556
|
+
"data-to": ind.siblingOf
|
|
2557
|
+
},
|
|
2558
|
+
[el("path", { d: path2 })]
|
|
2559
|
+
)
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2562
|
+
return group({ class: "schematex-genogram-sibling-of-edges" }, elements);
|
|
2563
|
+
}
|
|
2185
2564
|
function renderLabels(nodes, config) {
|
|
2186
2565
|
const labels = [];
|
|
2187
2566
|
for (const node of nodes) {
|
|
@@ -2238,5 +2617,5 @@ var genogram = {
|
|
|
2238
2617
|
};
|
|
2239
2618
|
|
|
2240
2619
|
export { ParseError, genogram, getRequiredDefs, layoutGenogram, parseGenogram, renderGenogram, renderIndividualSymbol };
|
|
2241
|
-
//# sourceMappingURL=chunk-
|
|
2242
|
-
//# sourceMappingURL=chunk-
|
|
2620
|
+
//# sourceMappingURL=chunk-SE23X5OE.js.map
|
|
2621
|
+
//# sourceMappingURL=chunk-SE23X5OE.js.map
|