Skip to content

Instantly share code, notes, and snippets.

@kjlubick
Last active March 12, 2019 02:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kjlubick/d25768f65c5f800a5a54dd16366021d9 to your computer and use it in GitHub Desktop.
Save kjlubick/d25768f65c5f800a5a54dd16366021d9 to your computer and use it in GitHub Desktop.
Family Tree Map
canvas.width = 3000;
canvas.height = 2200;
canvas.style.width = "900px";
const surface = CanvasKit.MakeCanvasSurface(canvas);
if (!surface) {
throw 'Could not make surface';
}
const skcanvas = surface.getCanvas();
const paint = new CanvasKit.SkPaint();
const font = new CanvasKit.SkFont(null, 20);
// FIXME: this is for zooming in and seeing the result better
//skcanvas.translate(-900, -800);
skcanvas.drawText("hello", 40, 40, paint, font);
paint.setStrokeWidth(2);
paint.setStyle(CanvasKit.PaintStyle.Stroke);
const OUTER_RADIUS = 1500;
const INNER_RADIUS = 150;
const MARGIN = 10;
const UNDER_ANGLE = 38;
// Midpoint line, just for reference
let path = new CanvasKit.SkPath();
path.moveTo(OUTER_RADIUS + MARGIN, MARGIN);
path.lineTo(OUTER_RADIUS + MARGIN, OUTER_RADIUS + MARGIN - INNER_RADIUS);
skcanvas.drawPath(path, paint);
path.delete();
/*
drawRing(OUTER_RADIUS, skcanvas, paint);
drawRing(INNER_RADIUS + 1000, skcanvas, paint);
drawRing(INNER_RADIUS + 700, skcanvas, paint);
//drawRing(INNER_RADIUS + 475, skcanvas, paint);
drawRing(INNER_RADIUS + 300, skcanvas, paint);
drawRing(INNER_RADIUS + 150, skcanvas, paint);
drawRing(INNER_RADIUS, skcanvas, paint);
// Grandparent gen 3
for (let i = 1; i < 4; i++) {
drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 2, INNER_RADIUS + 150, skcanvas, paint);
}
// Great grandparent gen 4
for (let i = 1; i < 8; i++) {
// drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 4, INNER_RADIUS + 300, skcanvas, paint);
}
// 2xGreat grandparent gen 5
for (let i = 1; i < 16; i++) {
drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 8, INNER_RADIUS + 475, skcanvas, paint);
}
// 3xGreat gen 6
for (let i = 1; i < 32; i++) {
drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 16, INNER_RADIUS + 700, skcanvas, paint);
}
// 4xGreat gen 7
for (let i = 1; i < 64; i++) {
drawRay(-UNDER_ANGLE + i * (UNDER_ANGLE + 90) / 32, INNER_RADIUS + 1000, skcanvas, paint);
}
*/
// TODO try drawing a branch at the grandparents level
let person = {
given_names: ['Joseph', 'Jordan', 'Fredrich'],
given_names_order: [1, 2, 0],
surnames: ['Henderson'],
birth_date: Date.UTC(1866, 3, 28),
birth_location: ['Trieste, Austria', 'Austria', 'AUT'],
death_date: Date.UTC(1904, 11, 30),
death_location: ['Chicago, Illinois', 'Chicago', 'IL'],
};
const ANGLE_4TH_LEVEL = 32;
rotateToCenter(skcanvas, 16 - 4*32, INNER_RADIUS + 475);
//draw4thGen(person);
skcanvas.restore();
rotateToCenter(skcanvas, 16 - 3*32, INNER_RADIUS + 475);
//draw4thGen(person);
skcanvas.restore();
rotateToCenter(skcanvas, 16 - 2*32, INNER_RADIUS + 475);
//draw4thGen(person);
skcanvas.restore();
rotateToCenter(skcanvas, 16 - 1*32, INNER_RADIUS + 475);
draw4thGen(person);
skcanvas.restore();
surface.flush();
function drawRing(r, canvas, paint) {
const cx = OUTER_RADIUS + MARGIN;
const cy = OUTER_RADIUS + MARGIN;
const arc = new CanvasKit.SkPath();
arc.moveTo(cx + INNER_RADIUS * Math.cos(toRad(UNDER_ANGLE)),
cy + INNER_RADIUS * Math.sin(toRad(UNDER_ANGLE)));
arc.arcTo(CanvasKit.LTRBRect(cx-r, cy-r, cx+r, cy+r), UNDER_ANGLE,
-180 - 2 * UNDER_ANGLE, false);
arc.lineTo(cx - INNER_RADIUS * Math.cos(toRad(UNDER_ANGLE)),
cy + INNER_RADIUS * Math.sin(toRad(UNDER_ANGLE)));
arc.close();
canvas.drawPath(arc, paint);
arc.delete();
}
function drawRay(angle, innerR, canvas, paint) {
const ray = new CanvasKit.SkPath();
const cx = OUTER_RADIUS + MARGIN;
const cy = OUTER_RADIUS + MARGIN;
ray.moveTo(cx - innerR * Math.cos(toRad(angle)),
cy - innerR * Math.sin(toRad(angle)));
ray.lineTo(cx - OUTER_RADIUS * Math.cos(toRad(angle)),
cy - OUTER_RADIUS * Math.sin(toRad(angle)));
canvas.drawPath(ray, paint);
ray.delete();
}
function toRad(degrees) {
return degrees * (Math.PI / 180);
}
function rotateToCenter(canvas, angleOfCenter, outerRadius) {
canvas.save();
const cx = OUTER_RADIUS + MARGIN;
const cy = OUTER_RADIUS + MARGIN;
canvas.translate(cx, cy);
canvas.rotate(angleOfCenter, 0, 0);
canvas.translate(0, -outerRadius);
}
function fitFirstName(person, font, maxWidth) {
const str = "J Jordan Freidrich";
return [str, font.measureText(str), 30];
}
function fitSurname(person, font, maxWidth) {
const str = "Henderson";
return [str, font.measureText(str), 32];
}
function fitLifetime(person, font, maxWidth) {
const str = "Apr 1866 - Dec 1904";
return [str, font.measureText(str), 24];
}
function fitLocation(person, font, maxWidth) {
const str = "Austria Chicago";
return [str, font.measureText(str), 24];
}
function fillArcSegment(widthDegrees, outerRadius, innerRadius, fillColor) {
// TODO this is a mess
const cx = outerRadius + MARGIN;
const cy = outerRadius + MARGIN;
const arc = new CanvasKit.SkPath();
const fillPaint = new CanvasKit.SkPaint();
// bottom left corner
arc.moveTo(cx + innerRadius * Math.cos(toRad(-widthDegrees/2 - 90)),
cy + innerRadius * Math.sin(toRad(-widthDegrees/2 - 90)));
arc.lineTo(cx + outerRadius * Math.cos(toRad(-widthDegrees/2 - 90)),
cy + outerRadius * Math.sin(toRad(-widthDegrees/2 - 90)));
//arc.arcTo(CanvasKit.LTRBRect(cx-outerRadius, cy-outerRadius,
// cx+outerRadius, cy+outerRadius),
// 0, widthDegrees, false);
arc.lineTo(cx + outerRadius * Math.cos(toRad(widthDegrees/2 - 90)),
cy + outerRadius * Math.sin(toRad(widthDegrees/2 - 90)));
arc.lineTo(cx + innerRadius * Math.cos(toRad(widthDegrees/2 - 90)),
cy + innerRadius * Math.sin(toRad(widthDegrees/2 - 90)));
arc.close();
skcanvas.drawPath(arc, paint);
arc.delete();
fillPaint.delete();
}
function draw4thGen(person) {
// draw background
const testPath = new CanvasKit.SkPath();
testPath.moveTo(10, 10);
testPath.lineTo(40, 10);
skcanvas.drawPath(testPath, paint);
fillArcSegment(32, INNER_RADIUS + 300, INNER_RADIUS+150, CanvasKit.BLACK);
// draw text
const textPaint = new CanvasKit.SkPaint();
textPaint.setStyle(CanvasKit.PaintStyle.Fill);
const namesFont = new CanvasKit.SkFont(null, 32);
const bottomFont = new CanvasKit.SkFont(null, 24);
const [gn, width1, size1] = fitFirstName(person, namesFont, "??");
const [sn, width2, size2] = fitSurname(person, namesFont, "??");
const [lt, width3, size3] = fitLifetime(person, bottomFont, "??");
const [loc, width4, size4] = fitLocation(person, bottomFont, "??");
namesFont.setSize(size1);
skcanvas.drawText(gn, -width1/2, 35, textPaint, namesFont);
namesFont.setSize(size2);
skcanvas.drawText(sn, -width2/2, 70, textPaint, namesFont);
bottomFont.setSize(size3);
skcanvas.drawText(lt, -width3/2, 100, textPaint, bottomFont);
bottomFont.setSize(size4);
skcanvas.drawText(loc, -width4/2, 125, textPaint, bottomFont);
textPaint.delete();
namesFont.delete();
bottomFont.delete();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment