Add simple tree graph mode #4
31
geneit_app/src/utils/render_utils.ts
Normal file
31
geneit_app/src/utils/render_utils.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
let canvas: HTMLCanvasElement = document.createElement("canvas");
|
||||||
|
let charLen: Map<string, Map<string, number>> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
|
||||||
|
*
|
||||||
|
* @param {String} text The text to be rendered.
|
||||||
|
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
|
||||||
|
*
|
||||||
|
* @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
|
||||||
|
*/
|
||||||
|
function computeTextWidth(text: string, font: string) {
|
||||||
|
// re-use canvas object for better performance
|
||||||
|
const context = canvas.getContext("2d")!;
|
||||||
|
context.font = font;
|
||||||
|
const metrics = context.measureText(text);
|
||||||
|
return metrics.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTextWidth(text: string, font: string) {
|
||||||
|
if (!charLen.has(font)) charLen.set(font, new Map());
|
||||||
|
const ref = charLen.get(font)!;
|
||||||
|
|
||||||
|
let size = 0;
|
||||||
|
for (const c of text) {
|
||||||
|
if (!ref.has(c)) ref.set(c, computeTextWidth(c, font));
|
||||||
|
size += ref.get(c)!;
|
||||||
|
}
|
||||||
|
console.log(text, size);
|
||||||
|
return size;
|
||||||
|
}
|
@ -2,18 +2,20 @@ import React from "react";
|
|||||||
import { Couple } from "../../api/CoupleApi";
|
import { Couple } from "../../api/CoupleApi";
|
||||||
import { Member } from "../../api/MemberApi";
|
import { Member } from "../../api/MemberApi";
|
||||||
import { FamilyTreeNode } from "../../utils/family_tree";
|
import { FamilyTreeNode } from "../../utils/family_tree";
|
||||||
|
import { getTextWidth } from "../../utils/render_utils";
|
||||||
|
|
||||||
const FACE_WIDTH = 60;
|
const FACE_WIDTH = 60;
|
||||||
const FACE_HEIGHT = 70;
|
const FACE_HEIGHT = 70;
|
||||||
|
const FACE_TEXT_SPACING = 2;
|
||||||
|
|
||||||
const CARD_HEIGHT = 110;
|
const CARD_HEIGHT = 103;
|
||||||
const SPOUSE_SPACING = 10;
|
const SPOUSE_SPACING = 10;
|
||||||
const CARD_SPACING = 20;
|
const CARD_SPACING = 20;
|
||||||
const SIBLINGS_SPACING = 20;
|
const SIBLINGS_SPACING = 20;
|
||||||
const LEVEL_SPACING = 20;
|
const LEVEL_SPACING = 25;
|
||||||
|
|
||||||
const NAME_CHAR_W = 7.1428;
|
const NAME_FONT = "13px Roboto";
|
||||||
const BIRTH_CHAR_W = 5;
|
const BIRTH_FONT = "10px Roboto";
|
||||||
|
|
||||||
interface SimpleTreeSpouseInfo {
|
interface SimpleTreeSpouseInfo {
|
||||||
member: Member;
|
member: Member;
|
||||||
@ -31,8 +33,8 @@ interface SimpleTreeNode {
|
|||||||
* Get the width of a member card
|
* Get the width of a member card
|
||||||
*/
|
*/
|
||||||
function memberCardWidth(m: Member): number {
|
function memberCardWidth(m: Member): number {
|
||||||
const nameWidth = m.fullName.length * NAME_CHAR_W;
|
const nameWidth = getTextWidth(m.fullName, NAME_FONT);
|
||||||
const birthDeathWidth = m.displayBirthDeath.length * BIRTH_CHAR_W;
|
const birthDeathWidth = getTextWidth(m.displayBirthDeath, BIRTH_FONT);
|
||||||
return Math.max(FACE_WIDTH, nameWidth, birthDeathWidth);
|
return Math.max(FACE_WIDTH, nameWidth, birthDeathWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +172,7 @@ function NodeArea(p: {
|
|||||||
node={n}
|
node={n}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
downXOffset += n.width;
|
downXOffset += n.width + SIBLINGS_SPACING;
|
||||||
return el;
|
return el;
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
@ -203,18 +205,20 @@ function MemberCard(p: {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Member text */}
|
{/* Member text */}
|
||||||
<text y={FACE_HEIGHT}>
|
<text y={FACE_HEIGHT + FACE_TEXT_SPACING}>
|
||||||
<tspan
|
<tspan
|
||||||
x={center(w, p.member.fullName.length * NAME_CHAR_W)}
|
x={center(w, getTextWidth(p.member.fullName, NAME_FONT))}
|
||||||
dy="14"
|
dy="14"
|
||||||
font-size="13"
|
font-size="13"
|
||||||
|
fontFamily="Roboto"
|
||||||
>
|
>
|
||||||
{p.member.fullName}
|
{p.member.fullName}
|
||||||
</tspan>
|
</tspan>
|
||||||
<tspan
|
<tspan
|
||||||
x={center(w, p.member.displayBirthDeath.length * BIRTH_CHAR_W)}
|
x={center(w, getTextWidth(p.member.displayBirthDeath, BIRTH_FONT))}
|
||||||
dy="14"
|
dy="14"
|
||||||
font-size="10"
|
font-size="10"
|
||||||
|
fontFamily="Roboto"
|
||||||
>
|
>
|
||||||
{p.member.displayBirthDeath}
|
{p.member.displayBirthDeath}
|
||||||
</tspan>
|
</tspan>
|
||||||
|
Loading…
Reference in New Issue
Block a user