Add simple tree graph mode #4

Merged
pierre merged 16 commits from simple_tree into master 2023-08-26 14:00:00 +00:00
2 changed files with 45 additions and 10 deletions
Showing only changes of commit e07b093dfc - Show all commits

View 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;
}

View File

@ -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>